Spring Security 快速上手
Spring Security 框架簡介
Spring Security 說明
- Spring Security 基于 Spring 框架,提供了一套 Web 應用安全性的完整解決方案
- 關于安全方面的兩個主要區域是“認證”和“授權”(或者訪問控制),一般來說,Web 應用的安全性包括用戶認證(Authentication)和用戶授權(Authorization)兩個部分,這兩點也是 Spring Security 重要核心功能。
- 用戶認證:驗證某個用戶是否為系統中的合法主體,也就是說用戶能否訪問該系統。用戶認證一般要求用戶提供用戶名和密碼。系統通過校驗用戶名和密碼來完成認證過程。通俗點說就是系統認為用戶是否能登錄。
- 用戶授權:驗證某個用戶是否有權限執行某個操作。在一個系統中,不同用戶所具有的權限是不同的。比如對一個文件來說,有的用戶只能進行讀取,而有的用戶可以進行修改。一般來說,系統會為不同的用戶分配不同的角色,而每個角色則對應一系列的權限。通俗點講就是系統判斷用戶是否有權限去做某些事情。
- 關于安全方面的兩個主要區域是“認證”和“授權”(或者訪問控制),一般來說,Web 應用的安全性包括用戶認證(Authentication)和用戶授權(Authorization)兩個部分,這兩點也是 Spring Security 重要核心功能。
Spring Security 官網
Spring Security 特點
- 和 Spring 無縫整合
- 重量級、全面的權限控制
- 專門為 Web 開發而設計
- 舊版本不能脫離 Web 環境使用。
- 新版本對整個框架進行了分層抽取,分成了核心模塊和 Web 模塊。單獨引入核心模塊就可以脫離 Web 環境。
Spring Security 對比 Shiro
Shiro 簡介
-
Shrio 是Apache 旗下的輕量級權限控制框架
特點:
- 輕量級。Shiro 主張的理念是把復雜的事情變簡單。針對對性能有更高要求的互聯網應用有更好表現。
- 通用性:
- 好處:不局限于 Web 環境,可以脫離 Web 環境使用。
- 缺陷:在 Web 環境下一些特定的需求需要手動編寫代碼定制。
總結
Spring Security 是 Spring 家族中的一個安全管理框架,實際上,在 Spring Boot 出現之前,Spring Security 就已經發展了多年了,但是使用的并不多,安全管理這個領域,一直是 Shiro 的天下。
相對于 Shiro,在 SSM 中整合 Spring Security 都是比較麻煩的操作,所以,SpringSecurity 雖然功能比 Shiro 強大,但是使用反而沒有 Shiro 多(Shiro 雖然功能沒有Spring Security 多,但是對于大部分項目而言,Shiro 也夠用了)。
自從有了 Spring Boot 之后,Spring Boot 對于 Spring Security 提供了自動化配置方案,可以使用更少的配置來使用 Spring Security。
因此,一般來說,常見的安全管理技術棧的組合是這樣的:
? SSM + Shiro
? Spring Boot/Spring Cloud + Spring Security
以上只是一個推薦的組合而已,如果單純從技術上來說,無論怎么組合,都是可以運行的。
Spring Security 框架快速入門
-
搭建spring boot 項目,并引入項目依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> -
新建Controller,并配置訪問方法
package org.taoguoguo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author taoGuoGuo * @description HelloController * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 14:16 */ @RestController @RequestMapping("/test") public class HelloController { @GetMapping("hello") public String hello(){ return "hello security"; } } -
訪問對應的方法,我們發現跳轉至了默認的Spring Security 登錄頁面,用戶名默認為 user , 密碼為 控制臺打印 password
Using generated security password: 22f1f943-b70b-43e9-bf56-adf37851a9e7
Spring Security 用戶名密碼配置
剛剛我們的密碼是在控制臺,Spring Security 內置實現的密碼登錄,那實際工作中我們怎么自己指定用戶名密碼呢?
常見的方式有三種:
- 通過配置文件指定
- 通過配置類指定
- 通過實現 UserDetailService 接口實現 數據庫查詢
通過配置文件指定
在 application.properties 中 進行配置
#第一種方式:通過配置文件配置Spring Security用戶名密碼
spring.security.user.name=taoguoguo
spring.security.user.password=taoguoguo
通過配置類實現 配置用戶名密碼
新建 SecurityConfig 并 實現 WebSecurityConfigurerAdapter 適配器實現配置重寫
package org.taoguoguo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author taoGuoGuo
* @description SecurityConfig
* @website http://www.rzrgm.cn/doondo
* @create 2021-06-11 14:55
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//如果缺少密碼解析 登陸后會返回當前登陸頁面 并報 id 為 null 的錯誤
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//方式二:通過配置類設置登錄用戶名和密碼,通過auth設置用戶名密碼
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("taoguoguo");
auth.inMemoryAuthentication().withUser("taoguoguo").password(password).roles("admin");
}
}
通過實現 UserDetailService 自定義實現
自定義實現類配置:
-
創建配置類,設置使用哪個userDetailService 實現類
package org.taoguoguo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @author taoGuoGuo * @description SecurityConfig * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 15:14 * SpringSecurity 自定義配置 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //設置自定義的數據庫實現及密碼解析器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } } -
編寫實現類,返回User對象,User對象有用戶名密碼和操作權限
package org.taoguoguo.service; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; 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; import java.util.List; /** * @author taoGuoGuo * @description MyUserDetailService * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 15:18 */ @Service("userDetailsService") public class MyUserDetailService implements UserDetailsService { /** * 自定義實現類方式查詢用戶名密碼 * @param s * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); //密碼 String password = passwordEncoder.encode("taoguoguo"); //權限 List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); return new User("taoguoguo", password, auths); } }
數據庫查詢認證
-
數據庫建立 users表
CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) COLLATE utf8mb4_general_ci NOT NULL, `password` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -
項目添加依賴 采用 mybatis-plus
<?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 https://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.5.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>org.taoguoguo</groupId> <artifactId>spring-security</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-security</name> <description>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-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-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> -
增加數據庫參數配置
server.port=8111 #數據庫配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/security_db?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root -
創建Users實體
package org.taoguoguo.entity; import lombok.Data; /** * @author taoGuoGuo * @description Users * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 15:51 */ @Data public class Users { private Integer id; private String username; private String password; } -
創建UsersMapper
package org.taoguoguo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; import org.taoguoguo.entity.Users; /** * @author taoGuoGuo * @description UsersMapper * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 15:53 */ @Repository public interface UsersMapper extends BaseMapper<Users> { } -
修改自定義配置實現類 MyUserDetailService 進行數據庫查詢
package org.taoguoguo.service; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; 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.stereotype.Service; import org.taoguoguo.entity.Users; import org.taoguoguo.mapper.UsersMapper; import java.util.List; /** * @author taoGuoGuo * @description MyUserDetailService * @website http://www.rzrgm.cn/doondo * @create 2021-06-11 15:18 */ @Service("userDetailsService") public class MyUserDetailService implements UserDetailsService { @Autowired private UsersMapper usersMapper; /** * 自定義實現類方式查詢用戶名密碼 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //調用usersMapper方法,根據用戶名查詢數據庫 QueryWrapper<Users> wrapper = new QueryWrapper<>(); wrapper.eq("username", username); Users users = usersMapper.selectOne(wrapper); if(users == null){ //數據庫沒有該用戶名 認證失敗 throw new UsernameNotFoundException("用戶名或密碼不存在!"); } //權限 List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role"); return new User(users.getUsername(), users.getPassword(), auths); } } -
在Spring Boot 配置啟動類中進行Mapper注解掃描配置 注入Mapper Bean
package org.taoguoguo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("org.taoguoguo.mapper") public class SpringSecurityApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityApplication.class, args); } }
Spring Security自定義登錄頁面 及 認證放行
通過重寫 webSecurityConfigureAdapter 的 configure(HttpSecurity http) 方法進行配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定義自己編寫登錄頁面
.loginPage("/login.html") //登錄頁面設置
.loginProcessingUrl("/user/login") //登錄訪問路徑
.defaultSuccessUrl("/test/index") //登錄成功后,跳轉路徑
.permitAll()
.and().authorizeRequests() //定義請求路徑訪問規則
.antMatchers("/","/test/hello","/user/login").permitAll() //設置不需要認證可以直接訪問的路徑
.anyRequest().authenticated() //除此之外任何請求都需要認證
.and().csrf().disable(); //關閉csrf防護
}
基于角色或權限的進行訪問控制
-
hasAuthority 方法,主要針對某一個權限進行控制訪問
如果當前的主體具有指定的權限,則返回 true,否則返回 false
修改配置類,增加
.antMatchers("/test/index").hasAuthority("admin")具備admin權限 才能訪問/test/index路徑@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //自定義自己編寫登錄頁面 .loginPage("/login.html") //登錄頁面設置 .loginProcessingUrl("/user/login") //登錄訪問路徑 .defaultSuccessUrl("/test/index") //登錄成功后,跳轉路徑 .permitAll() .and().authorizeRequests() //定義請求路徑訪問規則 .antMatchers("/","/test/hello","/user/login").permitAll() //設置不需要認證可以直接訪問的路徑 .antMatchers("/test/index").hasAuthority("admin") //具備admin權限才能訪問 /test/index資源 .anyRequest().authenticated() //除此之外任何請求都需要認證 .and().csrf().disable(); //關閉csrf防護 }那這個admin的權限是在哪里指定的呢?其實在之前我們已經提到過,在自定義的登錄邏輯中,我們會放置用戶所具備的權限信息
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //調用usersMapper方法,根據用戶名查詢數據庫 QueryWrapper<Users> wrapper = new QueryWrapper<>(); wrapper.eq("username", username); Users users = usersMapper.selectOne(wrapper); if(users == null){ //數據庫沒有該用戶名 認證失敗 throw new UsernameNotFoundException("用戶名或密碼不存在!"); } //權限 List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin"); return new User(users.getUsername(), users.getPassword(), auths); } -
hasAnyAuthority 方法
如果當前的主體有任何提供的角色(給定的作為一個逗號分隔的字符串列表)的話,返回true
修改配置類,增加
.antMatchers("/test/index").hasAnyAuthority("admin,manager")具備admin 或 manager其中任一權限 就能訪問/test/index路徑@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //自定義自己編寫登錄頁面 .loginPage("/login.html") //登錄頁面設置 .loginProcessingUrl("/user/login") //登錄訪問路徑 .defaultSuccessUrl("/test/index") //登錄成功后,跳轉路徑 .permitAll() .and().authorizeRequests() //定義請求路徑訪問規則 .antMatchers("/","/test/hello","/user/login").permitAll() //設置不需要認證可以直接訪問的路徑 .antMatchers("/test/index").hasAnyAuthority("admin,manager") .anyRequest().authenticated() //除此之外任何請求都需要認證 .and().csrf().disable(); //關閉csrf防護 } -
hasRole 如果用戶具備給定角色就允許訪問,否則出現 403 如果當前主體具有指定的角色,則返回 true。
查看源碼用法
// hasRole 取出角色相關信息 access 判斷是否能夠訪問 public ExpressionInterceptUrlRegistry hasRole(String role) { return access(ExpressionUrlAuthorizationConfigurer.hasRole(role)); } // 為role 底層拼接 ROLE_ 所以 我們在自定義登錄邏輯 賦予權限時 要拼接 ROLE_ private static String hasRole(String role) { Assert.notNull(role, "role cannot be null"); Assert.isTrue(!role.startsWith("ROLE_"), () -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'"); return "hasRole('ROLE_" + role + "')"; }修改配置類,配置用戶具備
deptManager才能訪問/test/index資源@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //自定義自己編寫登錄頁面 .loginPage("/login.html") //登錄頁面設置 .loginProcessingUrl("/user/login") //登錄訪問路徑 .defaultSuccessUrl("/test/index") //登錄成功后,跳轉路徑 .permitAll() .and().authorizeRequests() //定義請求路徑訪問規則 .antMatchers("/","/test/hello","/user/login").permitAll() //設置不需要認證可以直接訪問的路徑 .antMatchers("/test/index").hasRole("deptManager") .anyRequest().authenticated() //除此之外任何請求都需要認證 .and().csrf().disable(); //關閉csrf防護 }給用戶配置
deptManager角色權限@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //調用usersMapper方法,根據用戶名查詢數據庫 QueryWrapper<Users> wrapper = new QueryWrapper<>(); wrapper.eq("username", username); Users users = usersMapper.selectOne(wrapper); if(users == null){ //數據庫沒有該用戶名 認證失敗 throw new UsernameNotFoundException("用戶名或密碼不存在!"); } //權限 List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("manager,ROLE_deptManager"); return new User(users.getUsername(), users.getPassword(), auths); } -
hasAnyRole 表示用戶具備任何一個條件都可以訪問
用法和配置和 hasRole 一致,只不過在配置類中配置多個角色方可訪問資源路徑,同時給用戶角色信息時,給一個或多個即可
Spring Security 自定義403 無權限訪問頁面
-
我們在項目的靜態資源文件夾 static 下 新建 unauth.html 自定義的403頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自定義403頁面</title> </head> <body> 自定義403頁面! </body> </html> -
修改訪問配置類
http.exceptionHandling().accessDeniedPage("/unauth");
Spring Security 注解使用
@Secured 判斷是否具有角色
-
開啟Spring Security注解功能
@EnableGlobalMethodSecurity(securedEnabled = true)package org.taoguoguo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @SpringBootApplication @MapperScan("org.taoguoguo.mapper") @EnableGlobalMethodSecurity(securedEnabled = true) public class SpringSecurityApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityApplication.class, args); } } -
編寫需要 Secured 注解指定角色訪問的控制器
@GetMapping("testSecured") @Secured({"ROLE_sale","ROLE_admin"}) public String testSecured(){ return "hello Secured"; } -
在自定義登錄邏輯中,為用戶分配對應的角色信息
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("manager,ROLE_sale");
@PreAuthorize 進入方法前的權限驗證
-
開啟注解功能
@EnableGlobalMethodSecurity(prePostEnabled = true)package org.taoguoguo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @SpringBootApplication @MapperScan("org.taoguoguo.mapper") @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) public class SpringSecurityApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityApplication.class, args); } } -
編寫需要 preAuthorize 需要權限驗證的控制器
@RequestMapping("/preAuthorize") @PreAuthorize("hasAuthority('menu:system')") public String preAuthorize(){ return "Hello preAuthorize"; } -
在自定義登錄邏輯中,為用戶分配對應的角色信息
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("manager,menu:system,menu:create");
@PostAuthorize 在方法執行后再進行權限驗證,適合驗證帶有返回值的權限
用法和 preAuthorize 基本一致,比較簡單,這邊就不詳細說明了。重點明白什么場景下使用即可
@PostFilter 權限驗證之后對數據進行過濾
留下用戶名是 admin1 的數據 表達式中的 filterObject 引用的是方法返回值 List 中的某一個元素
@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_ 管理員')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
ArrayList<UserInfo> list = new ArrayList<>();
list.add(new UserInfo(1l,"admin1","6666"));
list.add(new UserInfo(2l,"admin2","888"));
return list;
}
@PreFilter 進入控制器之前對數據進行過濾
留下傳入參數 id 能對 2 整除的參數數據
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_ 管理員')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo> list){
list.forEach(t-> {
System.out.println(t.getId()+"\t"+t.getUsername());
});
return list;
}
Spring Security 實現 RememberMe
用戶流程及框架實現原理

具體實現方式
-
創建表,這個表也可以由程序自動創建
CREATE TABLE `persistent_logins` ( `username` varchar(64) NOT NULL, `series` varchar(64) NOT NULL, `token` varchar(64) NOT NULL, `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`series`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -
添加數據庫配置文件
#數據庫配置 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/security_db?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root -
修改配置類 SecurityConfig 中 注入數據源及配置數據庫操作對象
//注入數據源 @Autowired private DataSource dataSource; //注入數據源 配置操作數據庫對象 @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); //jdbcTokenRepository.setCreateTableOnStartup(true); //配置此配置則會自動幫我們建表 return jdbcTokenRepository; } -
開啟 Remeber me 功能
//開啟記住我功能 http.rememberMe().tokenRepository(persistentTokenRepository()) .tokenValiditySeconds(60*15) //有效時長 15分鐘 .userDetailsService(userDetailsService); -
login.html 頁面增加記住我 功能標簽 【注意:name 屬性值必須位 remember-me.不能改為其他值】
<form action="/user/login" method="post"> 用戶名:<input type="text" name="username"><br/> 密 碼:<input type="password" name="password"><br/> <input type="checkbox" name="remember-me" title="記住密碼">自動登錄<br/> <input type="submit" value="login"> </form>
最后附上完整的配置類代碼
package org.taoguoguo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
/**
* @author taoGuoGuo
* @description SecurityConfig
* @website http://www.rzrgm.cn/doondo
* @create 2021-06-11 15:14
* SpringSecurity 自定義配置
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
//注入數據源
@Autowired
private DataSource dataSource;
//注入數據源 配置操作數據庫對象
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
//jdbcTokenRepository.setCreateTableOnStartup(true); //配置此配置則會自動幫我們建表
return jdbcTokenRepository;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//設置自定義的數據庫實現及密碼解析器
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//表單認證
http.formLogin() //自定義自己編寫登錄頁面
.loginPage("/login.html") //登錄頁面設置
.loginProcessingUrl("/user/login") //登錄訪問路徑
.defaultSuccessUrl("/sys/loginSuccess") //登錄成功后,跳轉路徑
.permitAll()
.and().authorizeRequests() //定義請求路徑訪問規則
.antMatchers("/","/test/hello","/user/login").permitAll() //設置不需要認證可以直接訪問的路徑
//1 hasAuthority方法 具備admin權限才能訪問 /test/index資源
// .antMatchers("/test/index").hasAuthority("admin")
//2 hasAnyAuthority方法 具備admin,manager其中任一權限 才能訪問/test/index資源
// .antMatchers("/test/index").hasAnyAuthority("admin,manager")
.antMatchers("/test/index").hasRole("manager")
.anyRequest().authenticated(); //除此之外任何請求都需要認證
//開啟記住我功能
http.rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60*15) //有效時長 15分鐘
.userDetailsService(userDetailsService);
//關閉csrf防護
http.csrf().disable();
//用戶注銷配置
http.logout().logoutUrl("/logout").logoutSuccessUrl("/sys/logout").permitAll();
//自定義403頁面
http.exceptionHandling().accessDeniedPage("/sys/unauth403");
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

浙公網安備 33010602011771號