官方文檔參考,5.1.2 中文參考文檔,4.1 中文參考文檔,4.1 官方文檔中文翻譯與源碼解讀
SpringSecurity 核心功能:
- 認證(你是誰)
- 授權(你能干什么)
- 攻擊防護(防止偽造身份)
簡單的開始
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>
編寫一個最簡單的用戶 controller
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 和端口
server:
address: 127.0.0.1
port: 8081
logging:
level:
org.woodwhale.king: DEBUG
瀏覽器訪問http://127.0.0.1:8081/user,瀏覽器被自動重定向到了登錄的界面:
這個/login
訪問路徑在程序中沒有任何的顯示代碼編寫,為什么會出現這樣的界面呢,當前界面中的UI 都是哪里來的呢?
當然是 spring-security 進行了默認控制,從啟動日志中,可以看到一串用戶名默認為user
的默認密碼:
登錄成功之后,可以正常訪問服務資源了。
自定義默認用戶名和密碼
在配置文件配置用戶名和密碼:
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 可以是bcrypt
、sha256
等,后面緊跟着是使用這種加密類型進行加密后的密碼。
因此,程序接收到內存或者數據庫查詢到的密碼時,首先查找被{}
包括起來的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
,里面的屬性中最核心的就是password
,username
和authorities
。
自定義安全認證配置
配置自定義的登錄頁面:
@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 端就不行了, 所以我們還需要做進一步的處理。
這里做一下簡單的思路整理
這里提供一種思路,核心在於運用安全框架的:RequestCache
和RedirectStrategy
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 中的兩個接口AuthenticationSuccessHandler
和AuthenticationFailureHandler
。自定義這兩個接口的實現,並進行相應的配置就可以了。 當然框架是有默認的實現類的,我們可以繼承這個實現類再來自定義自己的業務:
成功登錄處理類
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 證書過期
參考資料:
https://blog.csdn.net/u013435893/article/details/79596628
https://blog.csdn.net/canon_in_d_major/article/details/79675033
https://juejin.im/post/5c46a49e51882528735ef2d9
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
https://www.cnblogs.com/yyxxn/p/8808851.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