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

木鯨魚發表於2019-04-14

官方文件參考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,瀏覽器被自動重定向到了登入的介面:

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

這個/login訪問路徑在程式中沒有任何的顯示程式碼編寫,為什麼會出現這樣的介面呢,當前介面中的UI 都是哪裡來的呢?

當然是 spring-security 進行了預設控制,從啟動日誌中,可以看到一串使用者名稱預設為user的預設密碼:

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

登入成功之後,可以正常訪問服務資源了。

自定義預設使用者名稱和密碼

在配置檔案配置使用者名稱和密碼:

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

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

上面這段話的意思是,現在新的 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頁面。

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

上述圖片中的配置有點小小缺陷,當去掉.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
複製程式碼

具體語法可檢視: github.com/thymeleaf/t…

常用的語法標籤

這裡為了表述方便,引用了上小節中的"自定義處理登入成功/失敗"的成功響應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    證照過期
複製程式碼

參考資料:

blog.csdn.net/u013435893/…

blog.csdn.net/canon_in_d_…

juejin.im/post/5c46a4…

www.jianshu.com/p/6307c89fe…

mp.weixin.qq.com/s/NKhwU6qKK…

mp.weixin.qq.com/s/sMi1__Rw_…

blog.csdn.net/smd25756245…

www.cnblogs.com/yyxxn/p/880…

blog.csdn.net/coder_py/ar…

參考專案原始碼:

github.com/whyalwaysme…

github.com/oycyqr/Spri…

github.com/chengjiansh…

個人部落格:woodwhale's blog

部落格園:木鯨魚的部落格

相關文章