SpringBoot + Spring Security 學習筆記(一)自定義基本使用及個性化登陸配置

官方文檔參考5.1.2 中文參考文檔4.1 中文參考文檔4.1 官方文檔中文翻譯與源碼解讀css

SpringSecurity 核心功能:html

  • 認證(你是誰)
  • 受權(你能幹什麼)
  • 攻擊防禦(防止僞造身份)

簡單的開始

pom 依賴前端

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.woodwhale.king</groupId>
    <artifactId>security-demo</artifactId>
    <version>1.0.0</version>
    <name>security-demo</name>
    <description>spring-security-demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

編寫一個最簡單的用戶 controllerjava

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping
    public String getUsers() {       
        return "Hello Spring Security";
    }
}

application.yml 配置IP 和端口mysql

server:
  address: 127.0.0.1
  port: 8081
  
logging:
  level:
    org.woodwhale.king: DEBUG

瀏覽器訪問http://127.0.0.1:8081/user,瀏覽器被自動重定向到了登陸的界面:git

這個/login訪問路徑在程序中沒有任何的顯示代碼編寫,爲何會出現這樣的界面呢,當前界面中的UI 都是哪裏來的呢?github

固然是 spring-security 進行了默認控制,從啓動日誌中,能夠看到一串用戶名默認爲user的默認密碼:web

登陸成功以後,能夠正常訪問服務資源了。spring

自定義默認用戶名和密碼

在配置文件配置用戶名和密碼:sql

spring:
  security:
    user:
      name: "admin"
      password: "admin"

關閉默認的安全訪問控制

舊版的 spring security 關閉默認安全訪問控制,只須要在配置文件中關閉便可:

security.basic.enabled = false

新版本 Spring-Boot2.xx(Spring-security5.x) 的再也不提供上述配置了:

方法1: 將 security 包從項目依賴中去除。

方法2:將org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration不注入spring中:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;

@SpringBootApplication
@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class})
public class SecurityDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecurityDemoApplication.class, args);
    }

}

方法3:己實現一個配置類繼承自WebSecurityConfigurerAdapter,並重寫configure(HttpSecurity http)方法:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/**").permitAll();
    }

    /**
    * 配置一個userDetailsService Bean
    * 再也不生成默認security.user用戶
    */
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return super.userDetailsService();
    }
}

注意:WebSecurityConfigurerAdapter是一個適配器類,因此爲了使自定義的配置類見名知義,因此寫成了WebSecurityConfig。同時增長了@EnableWebSecurity註解到了 spring security 中。

自定義用戶認證

安全認證配置注意事項

springsucrity 的自定義用戶認證配置的核心均在上述的WebSecurityConfigurerAdapter類中,用戶想要個性化的用戶認證邏輯,就須要本身寫一個自定義的配置類,適配到 spring security 中:

注意:若是配置了兩個以上的自定義實現類,那麼就會報WebSecurityConfigurers不惟一的錯誤:java.lang.IllegalStateException: @Order on WebSecurityConfigurers must be unique.

@Configuration
@EnableWebSecurity
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()               //  定義當須要提交表單進行用戶登陸時候,轉到的登陸頁面。
                .and()
                .authorizeRequests()   // 定義哪些URL須要被保護、哪些不須要被保護
                .anyRequest()          // 任何請求,登陸後能夠訪問
                .authenticated();
    }
}

自定義用戶名和密碼

密碼加密注意事項

將用戶名密碼設置到內存中,用戶登陸的時候會校驗內存中配置的用戶名和密碼:

在舊版本的 spring security 中,在上述自定義的BrowerSecurityConfig 中配置以下代碼便可:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
}

可是在新版本中,啓動運行都沒有問題,一旦用戶正確登陸的時候,會報異常:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

由於在 Spring security 5.0 中新增了多種加密方式,也改變了密碼的格式。官方文檔說明:Password Storage Format

上面這段話的意思是,如今新的 Spring Security 中對密碼的存儲格式是"{id}……"。前面的 id 是加密方式,id 能夠是bcryptsha256等,後面緊跟着是使用這種加密類型進行加密後的密碼。

所以,程序接收到內存或者數據庫查詢到的密碼時,首先查找被{}包括起來的id,以肯定後面的密碼是被什麼加密類型方式進行加密的,若是找不到就認爲 id 是 null。這也就是爲何程序會報錯:There is no PasswordEncoder mapped for the id "null"。官方文檔舉的例子中是各類加密方式針對同一密碼加密後的存儲形式,原始密碼都是"password"。

密碼加密

要想咱們的項目還可以正常登錄,須要將前端傳過來的密碼進行某種方式加密,官方推薦的是使用bcrypt加密方式(不用用戶使用相同原密碼生成的密文是不一樣的),所以須要在 configure 方法裏面指定一下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    auth.inMemoryAuthentication()
        .passwordEncoder(new BCryptPasswordEncoder())
        .withUser("admin")
        .password(new BCryptPasswordEncoder().encode("admin"))
        .roles("ADMIN");
}

固然還有一種方法,將passwordEncoder配置抽離出來:

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

自定義到內存

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin")
        .password(new BCryptPasswordEncoder().encode("admin"))
        .roles("ADMIN");
}

自定義到代碼

這裏還有一種更優雅的方法,實現org.springframework.security.core.userdetails.UserDetailsService接口,重載loadUserByUsername(String username)方法,當用戶登陸時,會調用UserDetailsService接口的loadUserByUsername()來校驗用戶的合法性(密碼和權限)。

這種方法爲以後結合數據庫或者JWT動態校驗打下技術可行性基礎。

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ADMIN"));
        return new User("root", new BCryptPasswordEncoder().encode("root"), authorities);
    }
    
}

固然,"自定義到內存"中的配置文件中的configure(AuthenticationManagerBuilder auth)配置就不須要再配置一遍了。

注意:對於返回的UserDetails實現類,可使用框架本身的 User,也能夠本身實現一個 UserDetails 實現類,其中密碼和權限都應該從數據庫中讀取出來,而不是寫死在代碼裏。

最佳實踐

將加密類型抽離出來,實現UserDetailsService接口,將二者注入到AuthenticationManagerBuilder中:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
}

UserDetailsService接口實現類:

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ADMIN"));
        return new User("root", new BCryptPasswordEncoder().encode("root"), authorities);
    }
    
}

這裏的 User 對象是框架提供的一個用戶對象,注意包名是:org.springframework.security.core.userdetails.User,裏面的屬性中最核心的就是passwordusernameauthorities

自定義安全認證配置

配置自定義的登陸頁面:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()                                // 定義當須要用戶登陸時候,轉到的登陸頁面。
        .loginPage("/login")                        // 設置登陸頁面
        .loginProcessingUrl("/user/login")          // 自定義的登陸接口
        .defaultSuccessUrl("/home").permitAll()     // 登陸成功以後,默認跳轉的頁面
        .and().authorizeRequests()                  // 定義哪些URL須要被保護、哪些不須要被保護
        .antMatchers("/", "/index","/user/login").permitAll()       // 設置全部人均可以訪問登陸頁面
        .anyRequest().authenticated()               // 任何請求,登陸後能夠訪問
        .and().csrf().disable();                    // 關閉csrf防禦
}

從上述配置中,能夠看出用能夠全部訪客都可以自由登陸//index進行資源訪問,同時配置了一個登陸的接口/lgoin,使用mvc作了視圖映射(映射到模板文件目錄中的login.html),controller 映射代碼太簡單就不贅述了,當用戶成功登陸以後,頁面會自動跳轉至/home頁面。

上述圖片中的配置有點小小缺陷,當去掉.loginProcessUrl()的配置的時候,登陸完畢,瀏覽器會一直重定向,直至報重定向失敗。由於登陸成功的 url 沒有配置成全部人都可以訪問,所以形成了死循環的結果。

所以,配置了登陸界面就須要配置任意可訪問:.antMatchers("/user/login").permitAll()

login.html代碼:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登陸頁面</title>
</head>
<body>
    <h2>自定義登陸頁面</h2>
    <form action="/user/login" method="post">
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密碼:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2"><button type="submit">登陸</button></td>
            </tr>
        </table>
    </form>
</body>
</html>

靜態資源忽略配置

上述配置用戶認證過程當中,會發現資源文件也被安全框架擋在了外面,所以須要進行安全配置:

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/webjars/**/*", "/**/*.css", "/**/*.js");
}

如今前端框架的靜態資源徹底能夠經過webjars統一管理,所以注意配置/webjars/**/*

處理不一樣類型的請求

先後端分離的系統中,通常後端僅提供接口 JSON 格式的數據,以供前端自行調用。剛纔那樣,調用了被保護的接口,直接進行了頁面的跳轉,在web端還能夠接受,可是在 App 端就不行了, 因此咱們還須要作進一步的處理。
這裏作一下簡單的思路整理

這裏提供一種思路,核心在於運用安全框架的:RequestCacheRedirectStrategy

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class BrowserSecurityController {

    // 原請求信息的緩存及恢復
    private RequestCache requestCache = new HttpSessionRequestCache();

    // 用於重定向
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    /**
     * 當須要身份認證的時候,跳轉過來
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/authentication/require")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public String requireAuthenication(HttpServletRequest request, HttpServletResponse response) throws IOException {
        SavedRequest savedRequest = requestCache.getRequest(request, response);

        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            log.info("引起跳轉的請求是:" + targetUrl);
            if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                redirectStrategy.sendRedirect(request, response, "/login.html");
            }
        }

        return "訪問的服務須要身份認證,請引導用戶到登陸頁";
    }
}

注意:這個/authentication/require須要配置到安全認證配置:配置成默認登陸界面,並設置成任何人都可以訪問,而且這個重定向的頁面能夠設計成配置,從配置文件中讀取。

自定義處理登陸成功/失敗

在先後端分離的狀況下,咱們登陸成功了可能須要向前端返回用戶的我的信息,而不是直接進行跳轉。登陸失敗也是一樣的道理。這裏涉及到了 Spring Security 中的兩個接口AuthenticationSuccessHandlerAuthenticationFailureHandler。自定義這兩個接口的實現,並進行相應的配置就能夠了。 固然框架是有默認的實現類的,咱們能夠繼承這個實現類再來自定義本身的業務:

成功登陸處理類

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component("myAuthenctiationSuccessHandler")
public class MyAuthenctiationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
            HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        log.info("登陸成功");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(authentication));
    }
}

成功登陸以後,經過 response 返回一個 JSON 字符串回去。這個方法中的第三個參數Authentication,它裏面包含了登陸後的用戶信息(UserDetails),Session 的信息,登陸信息等。

登陸成功以後的響應JSON:

{
    "authorities": [
        {
            "authority": "ROLE_admin"
        }
    ],
    "details": {
        "remoteAddress": "127.0.0.1",
        "sessionId": "8BFA4F61A7CEA774C00F616AAE8C307C"
    },
    "authenticated": true,
    "principal": {
        "password": null,
        "username": "admin",
        "authorities": [
            {
                "authority": "ROLE_admin"
            }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "credentials": null,
    "name": "admin"
}

這裏有個細節須要注意:

principal 中有個權限數組集合authorities,裏面的權限值是:ROLE_admin,而自定義的安全認證配置中配置的是:admin,因此ROLE_前綴是框架本身加的,後期取出權限集合的時候須要注意這個細節,以取決於判斷是否有權限是使用字符串的包含關係仍是等值關係。

登陸失敗處理類

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component("myAuthenctiationFailureHandler")
public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {

        log.info("登陸失敗");
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
    }
}

將兩個自定義的處理類配置到自定義配置文件中:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.woodwhale.king.handler.MyAuthenctiationFailureHandler;
import org.woodwhale.king.handler.MyAuthenctiationSuccessHandler;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;

    @Autowired
    private MyAuthenctiationSuccessHandler myAuthenctiationSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()                                // 定義當須要用戶登陸時候,轉到的登陸頁面。
            .loginPage("/login")                        // 設置登陸頁面
            .loginProcessingUrl("/user/login")          // 自定義的登陸接口
            .successHandler(myAuthenctiationSuccessHandler)
            .failureHandler(myAuthenctiationFailureHandler)
            //.defaultSuccessUrl("/home").permitAll()       // 登陸成功以後,默認跳轉的頁面
            .and().authorizeRequests()                  // 定義哪些URL須要被保護、哪些不須要被保護
            .antMatchers("/", "/index").permitAll()     // 設置全部人均可以訪問登陸頁面
            .anyRequest().authenticated()               // 任何請求,登陸後能夠訪問
            .and().csrf().disable();                    // 關閉csrf防禦
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .passwordEncoder(new BCryptPasswordEncoder()).withUser("admin")
            .password(new BCryptPasswordEncoder().encode("admin"))
            .roles("admin");
    }

}

注意:defaultSuccessUrl不須要再配置了,實測若是配置了,成功登陸的 handler 就不起做用了。

小結

能夠看出,經過自定義的登陸成功或者失敗類,進行登陸響應控制,能夠設計一個配置,以靈活適配響應返回的是頁面仍是 JSON 數據。

結合thymeleaf

在前端使用了Thymeleaf進行渲染,特使是結合Spring Security在前端獲取用戶信息

依賴添加:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

注意:

由於本項目使用了spring boot 自動管理版本號,因此引入的必定是徹底匹配的,若是是舊的 spring security 版本須要手動引入對應的版本。

引用官方版本引用說明:

thymeleaf-extras-springsecurity3 for integration with Spring Security 3.x
thymeleaf-extras-springsecurity4 for integration with Spring Security 4.x
thymeleaf-extras-springsecurity5 for integration with Spring Security 5.x

具體語法可查看:
https://github.com/thymeleaf/thymeleaf-extras-springsecurity

經常使用的語法標籤

這裏爲了表述方便,引用了上小節中的"自定義處理登陸成功/失敗"的成功響應JSON數據:

{
    "authorities": [
        {
            "authority": "ROLE_admin"
        }
    ],
    "details": {
        "remoteAddress": "127.0.0.1",
        "sessionId": "8BFA4F61A7CEA774C00F616AAE8C307C"
    },
    "authenticated": true,
    "principal": {
        "password": null,
        "username": "admin",
        "authorities": [
            {
                "authority": "ROLE_admin"
            }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "credentials": null,
    "name": "admin"
}

sec:authorize="isAuthenticated():判斷是否有認證經過

sec:authorize="hasRole('ROLE_ADMIN')"判斷是否有ROLE_ADMIN權限

注意:上述的hasRole()標籤使用能成功的前提是:自定義用戶的權限字符集必須是以ROLE_爲前綴的,不然解析不到,即自定義的UserDetailsService 實現類的返回用戶的權限數組列表的權限字段必須是ROLE_***,同時在 html 頁面中注意引入對應的xmlns,本例這裏引用了:

<html xmlns:th="http://www.thymeleaf.org"
    xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

sec:authentication="principal.authorities":獲得該用戶的全部權限列表

sec:authentication="principal.username":獲得該用戶的用戶名

固然也能夠獲取更多的信息,只要UserDetailsService實現類中返回的用戶中攜帶有的信息都可以獲取。

常見異常類

AuthenticationException        經常使用的的子類:(會被底層換掉,不推薦使用)
UsernameNotFoundException      用戶找不到
BadCredentialsException        壞的憑據
AccountStatusException         用戶狀態異常它包含以下子類:(推薦使用)
AccountExpiredException        帳戶過時
LockedException                帳戶鎖定
DisabledException              帳戶不可用
CredentialsExpiredException    證書過時

參考資料:

http://www.javashuo.com/article/p-rcacajeh-v.html

https://blog.csdn.net/canon_in_d_major/article/details/79675033

http://www.javashuo.com/article/p-zkzkkryg-cc.html

https://www.jianshu.com/p/6307c89fe3fa/

https://mp.weixin.qq.com/s/NKhwU6qKKU0Q0diA0hg13Q

https://mp.weixin.qq.com/s/sMi1__Rw_s75YDaIdmTWKw

https://blog.csdn.net/smd2575624555/article/details/82759863

http://www.javashuo.com/article/p-bafwqlds-kk.html

https://blog.csdn.net/coder_py/article/details/80330868

參考項目源碼:

https://github.com/whyalwaysmea/Spring-Security

https://github.com/oycyqr/SpringSecurity

https://github.com/chengjiansheng/cjs-springsecurity-example

相關文章
相關標籤/搜索