Spring Security基于JWT登錄認(rèn)證的項(xiàng)目實(shí)踐
一言以蔽之,JWT 可以攜帶非敏感信息,并具有不可篡改性??梢酝ㄟ^(guò)驗(yàn)證是否被篡改,以及讀取信息內(nèi)容,完成網(wǎng)絡(luò)認(rèn)證的三個(gè)問(wèn)題:“你是誰(shuí)”、“你有哪些權(quán)限”、“是不是冒充的”。 為了安全,使用它需要采用 Https 協(xié)議,并且一定要小心防止用于加密的密鑰泄露。
采用 JWT 的認(rèn)證方式下,服務(wù)端并不存儲(chǔ)用戶(hù)狀態(tài)信息,有效期內(nèi)無(wú)法廢棄,有效期到期后,需要重新創(chuàng)建一個(gè)新的來(lái)替換。 所以它并不適合做長(zhǎng)期狀態(tài)保持,不適合需要用戶(hù)踢下線(xiàn)的場(chǎng)景,不適合需要頻繁修改用戶(hù)信息的場(chǎng)景。因?yàn)橐鉀Q這些問(wèn)題,總是需要額外查詢(xún)數(shù)據(jù)庫(kù)或者緩存,或者反復(fù)加密解密,強(qiáng)扭的瓜不甜,不如直接使用 Session。不過(guò)作為服務(wù)間的短時(shí)效切換,還是非常合適的,就比如 OAuth 之類(lèi)的。
目標(biāo)功能點(diǎn)
通過(guò)填寫(xiě)用戶(hù)名和密碼登錄。
- 通過(guò)填寫(xiě)用戶(hù)名和密碼登錄。
- 驗(yàn)證成功后, 服務(wù)端生成 JWT 認(rèn)證 token, 并返回給客戶(hù)端。
- 驗(yàn)證失敗后返回錯(cuò)誤信息。
- 客戶(hù)端在每次請(qǐng)求中攜帶 JWT 來(lái)訪(fǎng)問(wèn)權(quán)限內(nèi)的接口。
- 每次請(qǐng)求驗(yàn)證 token 有效性和權(quán)限,在無(wú)有效 token 時(shí)拋出 401 未授權(quán)錯(cuò)誤。
- 當(dāng)發(fā)現(xiàn)請(qǐng)求帶著的 token 有效期快到了的時(shí)候,返回特定狀態(tài)碼,重新請(qǐng)求一個(gè)新 token。
Spring Security是Spring家族中的安全框架,可以用來(lái)做用戶(hù)驗(yàn)證和權(quán)限管理等。Spring Security是一款重型框架,不過(guò)功能十分強(qiáng)大。一般來(lái)說(shuō),如果項(xiàng)目中需要進(jìn)行權(quán)限管理,具有多個(gè)角色和多種權(quán)限,我們可以使用Spring Security。SpringSecurity 采用的是責(zé)任鏈的設(shè)計(jì)模式,是一堆過(guò)濾器鏈的組合,它有一條很長(zhǎng)的過(guò)濾器鏈。
準(zhǔn)備工作
引入 Maven 依賴(lài)
針對(duì)這個(gè)登錄驗(yàn)證的實(shí)現(xiàn),需要引入 Spring Security、jackson、java-jwt 三個(gè)包。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.12.1</version> </dependency>
配置 DAO 數(shù)據(jù)層
要驗(yàn)證用戶(hù)前,自然是先要?jiǎng)?chuàng)建用戶(hù)實(shí)體對(duì)象,以及獲取用戶(hù)的服務(wù)類(lèi)。不同的是,這兩個(gè)類(lèi)需要實(shí)現(xiàn) Spring Security 的接口,以便將它們集成到驗(yàn)證框架中。
User
用戶(hù)實(shí)體類(lèi)需要實(shí)現(xiàn) ”UserDetails“ 接口,這個(gè)接口要求實(shí)現(xiàn) getUsername
、getPassword
、getAuthorities
三個(gè)方法,用以獲取用戶(hù)名、密碼和權(quán)限。以及 isAccountNonExpired
`isAccountNonLocked
、isCredentialsNonExpired
、isEnabled
這四個(gè)判斷是否是有效用戶(hù)的方法,因?yàn)楹万?yàn)證無(wú)關(guān),所以先都返回 true
。這里圖方便,用了 lombok
。
@Data public class User implements UserDetails { private static final long serialVersionUID = 1L; private String username; private String password; private Collection<? extends GrantedAuthority> authorities; ... }
UserService
用戶(hù)服務(wù)類(lèi)需要實(shí)現(xiàn) “UserDetailsService” 接口,這個(gè)接口非常簡(jiǎn)單,只需要實(shí)現(xiàn) loadUserByUsername(String username)
這么一個(gè)方法。這里使用了 MyBatis 來(lái)連接數(shù)據(jù)庫(kù)獲取用戶(hù)信息。
@Service public class UserService implements UserDetailsService { @Autowired UserMapper userMapper; @Override @Transactional public User loadUserByUsername(String username) { return userMapper.getByUsername(username); } ... }
創(chuàng)建 JWT 工具類(lèi)
這個(gè)工具類(lèi)主要負(fù)責(zé) token 的生成,驗(yàn)證,從中取值。
@Component public class JwtTokenProvider { private static final long JWT_EXPIRATION = 5 * 60 * 1000L; // 五分鐘過(guò)期 public static final String TOKEN_PREFIX = "Bearer "; // token 的開(kāi)頭字符串 private String jwtSecret = "XXX 密鑰,打死也不能告訴別人"; ... }
生成 JWT:從以通過(guò)驗(yàn)證的認(rèn)證對(duì)象中,獲取用戶(hù)信息,然后用指定加密方式,以及過(guò)期時(shí)間生成 token。這里簡(jiǎn)單的只加了用戶(hù)名這一個(gè)信息到 token 中:
public String generateToken(Authentication authentication) { User userPrincipal = (User) authentication.getPrincipal(); // 獲取用戶(hù)對(duì)象 Date expireDate = new Date(System.currentTimeMillis() + JWT_EXPIRATION); // 設(shè)置過(guò)期時(shí)間 try { Algorithm algorithm = Algorithm.HMAC256(jwtSecret); // 指定加密方式 return JWT.create().withExpiresAt(expireDate).withClaim("username", userPrincipal.getUsername()) .sign(algorithm); // 簽發(fā) JWT } catch (JWTCreationException jwtCreationException) { return null; } }
驗(yàn)證 JWT:指定和簽發(fā)相同的加密方式,驗(yàn)證這個(gè) token 是否是本服務(wù)器簽發(fā),是否篡改,或者已過(guò)期。
public boolean validateToken(String authToken) { try { Algorithm algorithm = Algorithm.HMAC256(jwtSecret); // 和簽發(fā)保持一致 JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(authToken); return true; } catch (JWTVerificationException jwtVerificationException) { return false; } }
獲取荷載信息:從 token 的荷載部分里解析用戶(hù)名信息,這部分是 md5 編碼的,屬于公開(kāi)信息。
public String getUsernameFromJWT(String authToken) { try { DecodedJWT jwt = JWT.decode(authToken); return jwt.getClaim("username").asString(); } catch (JWTDecodeException jwtDecodeException) { return null; } }
登錄
登錄部分需要?jiǎng)?chuàng)建三個(gè)文件:負(fù)責(zé)登錄接口處理的攔截器,登陸成功或者失敗的處理類(lèi)。
LoginFilter
Spring Security 默認(rèn)自帶表單登錄,負(fù)責(zé)處理這個(gè)登錄驗(yàn)證過(guò)程的過(guò)濾器叫“UsernamePasswordAuthenticationFilter”,不過(guò)它只支持表單傳值,這里用自定義的類(lèi)繼承它,使其能夠支持 JSON 傳值,負(fù)責(zé)登錄驗(yàn)證接口。
這個(gè)攔截器只需要負(fù)責(zé)從請(qǐng)求中取值即可,驗(yàn)證工作 Spring Security 會(huì)幫我們處理好。
參考文章
SpringBoot整合Spring Security + JWT實(shí)現(xiàn)用戶(hù)認(rèn)證-阿里云開(kāi)發(fā)者社區(qū)
到此這篇關(guān)于Spring Security基于JWT登錄認(rèn)證的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)Spring Security JWT登錄認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud Feign轉(zhuǎn)發(fā)請(qǐng)求頭(防止session失效)的解決方案
這篇文章主要介紹了SpringCloud Feign轉(zhuǎn)發(fā)請(qǐng)求頭(防止session失效)的解決方案,本文給大家分享兩種解決方案供大家參考,感興趣的朋友跟隨小編一起看看吧2020-10-10用java開(kāi)發(fā)dota英雄最華麗的技能(實(shí)例講解)
下面小編就為大家分享一篇使用java開(kāi)發(fā)dota英雄最華麗的技能實(shí)例,具有非常好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Ubuntu下配置Tomcat服務(wù)器以及設(shè)置自動(dòng)啟動(dòng)的方法
這篇文章主要介紹了Ubuntu下配置Tomcat服務(wù)器以及設(shè)置自動(dòng)啟動(dòng)的方法,適用于Java的web程序開(kāi)發(fā),需要的朋友可以參考下2015-10-10Spring+Quartz配置定時(shí)任務(wù)實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring+Quartz配置定時(shí)任務(wù)實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04詳解springboot shiro jwt實(shí)現(xiàn)權(quán)限管理
為什么使用jwt呢,因?yàn)榭梢酝ㄟ^(guò)URL,POST參數(shù)或者在HTTP header發(fā)送,因?yàn)閿?shù)據(jù)量小,傳輸速度也很快。本篇通過(guò)具體代碼來(lái)進(jìn)行詳情解析,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值2021-09-09Spring高級(jí)注解@PropertySource詳細(xì)解讀
這篇文章主要介紹了Spring高級(jí)注解@PropertySource詳細(xì)解讀,@PropertySource注解用于指定資源文件讀取的位置,它不僅能讀取properties文件,也能讀取xml文件,并且通過(guò)YAML解析器,配合自定義PropertySourceFactory實(shí)現(xiàn)解析yaml文件,需要的朋友可以參考下2023-11-11Java 實(shí)現(xiàn)多線(xiàn)程切換等待喚醒交替打印奇偶數(shù)
這篇文章主要介紹了Java 實(shí)現(xiàn)多線(xiàn)程切換等待喚醒交替打印奇偶數(shù) ,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05