javascript
Spring Security关于用户身份认证与授权
Spring Security是用于解決認證與授權的框架。
創建spring項目,添加依賴
如:
<?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>cn.tedu</groupId><artifactId>csmall-server</artifactId><version>0.0.1-SNAPSHOT</version></parent><!-- 當前項目的信息 --><groupId>cn.tedu</groupId><artifactId>csmall-passport</artifactId><version>0.0.1-SNAPSHOT</version><!-- 當前項目需要使用的依賴項 --><dependencies><!-- Spring Boot Web:支持Spring MVC --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Security:處理認證與授權 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- Spring Boot Test:測試 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>調整完成后,即可啟動項目,在啟動的日志中,可以看到類似以下內容:
Using generated security password: 2abb9119-b5bb-4de9-8584-9f893e4a5a92Spring Security有默認登錄的賬號和密碼(以上提示的值),密碼是隨機的,每次啟動項目都會不同。
Spring Security默認要求所有的請求都是必須先登錄才允許的訪問,可以使用默認的用戶名user和自動生成的隨機密碼來登錄。在測試登錄時,在瀏覽器訪問當前主機的任意網址都可以(包括不存在的資源),會自動跳轉到登錄頁(是由Spring Security提供的,默認的URL是:http://localhost:8080/login),當登錄成功后,會自動跳轉到此前訪問的URL(跳轉登錄頁之前的URL),另外,還可以通過 http://localhost:8080/logout 退出登錄。
Spring Security的依賴項中包括了Bcrypt算法的工具類,Bcrypt是一款非常優秀的密碼加密工具,適用于對需要存儲下來的密碼進行加密處理。
public class BcryptPasswordEncoderTests {private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();@Testpublic void testEncode() {// 原文相同的情況,每次加密得到的密文都不同for (int i = 0; i < 10; i++) {String rawPassword = "123456";String encodedPassword = passwordEncoder.encode(rawPassword);System.out.println("rawPassword = " + rawPassword);System.out.println("encodedPassword = " + encodedPassword);}// rawPassword = 123456// encodedPassword = $2a$10$HWuJ9WgPazrwg9.isaae4u7XdP7ohH7LetDwdlTWuPC4ZAvG.Uc7W// encodedPassword = $2a$10$rOwgZMpDvZ3Kn7CxHWiEbeC6bQMGtfX.VYc9DCzx9BxkWymX6FbrS// encodedPassword = $2a$10$H8ehVGsZx89lSVHwBVI37OkxWm8LXei4T1o5of82Hwc1rD0Yauhky// encodedPassword = $2a$10$meBbCiHZBcYn7zMrZ4fPd.hizrsiZhAu8tmDk.P8QJcCzSQGhXSvq// encodedPassword = $2a$10$bIRyvV29aoeJLo6hh1M.yOvKoOud5kC7AXDMSUW4tF/DlcG0bLj9C// encodedPassword = $2a$10$eq5BuoAiQ6Uo0.TOPZOFPuRNlPl3t2GoTlaFoYfBu3/Bo3tLzx.v2// encodedPassword = $2a$10$DhTSwQfNdqrGgHRmILmNLeV0jt3ZXL435xz0fwyZ315ciI5AuI5gi// encodedPassword = $2a$10$T.8/ISoLOdreEEkp4py36O0ZYfihDbdHDuIElZVF3uEgMOX.8sPcK// encodedPassword = $2a$10$hI4wweFOGJ7FMduSmcjNBexbKFOjYMWl8hkug0n0k1LNR5vEyhhMW// encodedPassword = $2a$10$b4ztMI6tWoiJuoDYKwr7DOywsPkkCdvDxbPfmEsLdp11NdABS7wyy}@Testpublic void testMatches() {String rawPassword = "123456";String encodedPassword = "$2a$10$hI4wweFOGJ7FMduSmCjNBexbKFOjYMWl8hkug0n0k1LNR5vEyhhMW";boolean matchResult = passwordEncoder.matches(rawPassword, encodedPassword);System.out.println("match result : " + matchResult);}}如果要使得Spring Security能使用數據庫中的信息(數據庫中的用戶名與密碼)來驗證用戶身份(認證),首先,必須實現“根據用戶名查詢此用戶的登錄信息(應該包括權限信息)”的查詢功能,要實現此查詢,需要執行的SQL語句大致是:
select ams_admin.id, ams_admin.username, ams_admin.password, ams_admin.is_enable, ams_permission.value from ams_admin left join ams_admin_role on ams_admin.id = ams_admin_role.admin_id left join ams_role_permission on ams_admin_role.role_id = ams_role_permission.role_id left join ams_permission on ams_role_permission.permission_id = ams_permission.id where username='root';Spring Security的認證機制中包含:當客戶端提交登錄后,會自動調用UserDetailsService接口(Spring Security定義的)的實現類對象中的UserDetails loadUserByUsername(String username)方法(根據用戶名加載用戶數據),將得到UserDetails類型的對象,此對象中應該至少包括此用戶名對應的密碼、權限等信息,接下來,Spring Security會自動完成密碼的對比,并確定此次客戶端提交的信息是否允許登錄!類似于:
// Spring Security的行為 UserDetails userDetails = userDetailsService.loadUserByUsername("chengheng"); // Spring Security將從userDetails中獲取密碼,用于驗證客戶端提交的密碼,判斷是否匹配所以,要實現Spring Security通過數據庫的數據來驗證用戶名與密碼(而不是采用默認的user用戶名和隨機的密碼),則在cn.tedu.csmall.passport包下創建security.UserDetailsServiceImpl類,實現UserDetailsService接口,并重寫接口中的抽象方法:
package cn.tedu.csmall.passport.security;import cn.tedu.csmall.passport.mapper.AdminMapper; import cn.tedu.csmall.pojo.vo.AdminLoginVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; 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;@Service public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate AdminMapper adminMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {System.out.println("根據用戶名查詢嘗試登錄的管理員信息,用戶名=" + s);AdminLoginVO admin = adminMapper.getLoginInfoByUsername(s);System.out.println("通過持久層進行查詢,結果=" + admin);if (admin == null) {System.out.println("根據用戶名沒有查詢到有效的管理員數據,將拋出異常");throw new BadCredentialsException("登錄失敗,用戶名不存在!");}System.out.println("查詢到匹配的管理員數據,需要將此數據轉換為UserDetails并返回");UserDetails userDetails = User.builder().username(admin.getUsername()).password(admin.getPassword()).accountExpired(false).accountLocked(false).disabled(admin.getIsEnable() != 1).credentialsExpired(false).authorities(admin.getPermissions().toArray(new String[] {})).build();System.out.println("轉換得到UserDetails=" + userDetails);return userDetails;}}完成后,再配置密碼加密器即可:
package cn.tedu.csmall.passport.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;@Configuration public class SecurityConfiguration {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}}重啟項目,可以發現在啟動過程中不再生成隨機的密碼值,在瀏覽器上訪問此項目的任何URL,進入登錄頁,即可使用數據庫中的管理員數據進行登錄。
在Spring Security,默認使用Session機制存儲成功登錄的用戶信息(因為HTTP協議是無狀態協議,并不保存客戶端的任何信息,所以,同一個客戶端的多次訪問,對于服務器而言,等效于多個不同的客戶端各訪問一次,為了保存用戶信息,使得服務器端能夠識別客戶端的身份,必須采取某種機制),當下,更推薦使用Token或相關技術(例如JWT)來解決識別用戶身份的問題。
JWT = JSON Web Token,它是通過JSON格式組織必要的數據,將數據記錄在票據(Token)上,并且,結合一定的算法,使得這些數據會被加密,然后在網絡上傳輸,服務器端收到此數據后,會先對此數據進行解密,從而得到票據上記錄的數據(JSON數據),從而識別用戶的身份,或者處理相關的數據。
其實,在客戶端第1次訪問服務器端時,是“空著手”訪問的,不會攜帶任何票據數據,當服務器進行響應時,會將JWT響應到客戶端,客戶端從第2次訪問開始,每次都應該攜帶JWT發起請求,則服務器都會收到請求中的JWT并進行處理。
要使用JWT,需要添加相關的依賴項,可以實現生成JWT、解析JWT的框架較多,目前,主流的JWT框架可以是jjwt:
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency>總結
以上是生活随笔為你收集整理的Spring Security关于用户身份认证与授权的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql查询每个用户最新的一条订单
- 下一篇: 新版正方教务管理系统API(获取课程表,