日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringSecurity 整合 JWT

發(fā)布時間:2023/12/10 javascript 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringSecurity 整合 JWT 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

項目集成Spring Security(一)

在上一篇基礎(chǔ)上繼續(xù)集成 JWT ,實現(xiàn)用戶身份驗證。

前言

前后端分離項目中,如果直接把 API 接口對外開放,我們知道這樣風(fēng)險是很大的,所以在上一篇中我們引入了 Spring Security ,但是我們在登陸后缺少了請求憑證部分。

什么是JWT?

JWT是 Json Web Token 的縮寫。它是基于 RFC 7519 標(biāo)準(zhǔn)定義的一種可以安全傳輸?shù)?小巧 和 自包含 的JSON對象。由于數(shù)據(jù)是使用數(shù)字簽名的,所以是可信任的和安全的。JWT可以使用HMAC算法對secret進(jìn)行加密或者使用RSA的公鑰私鑰對來進(jìn)行簽名。

JWT的工作流程

1、用戶進(jìn)入登錄頁,輸入用戶名、密碼,進(jìn)行登錄;
2、服務(wù)器驗證登錄鑒權(quán),如果改用戶合法,根據(jù)用戶的信息和服務(wù)器的規(guī)則生成 JWT Token
3、服務(wù)器將該 token 以 json 形式返回(不一定要json形式,這里說的是一種常見的做法)
4、用戶得到 token,存在 localStorage、cookie 或其它數(shù)據(jù)存儲形式中。以后用戶請求 /protected 中的 API 時,在請求的 header 中加入 Authorization: Bearer xxxx(token)。此處注意token之前有一個7字符長度的 Bearer。
5、服務(wù)器端對此 token 進(jìn)行檢驗,如果合法就解析其中內(nèi)容,根據(jù)其擁有的權(quán)限和自己的業(yè)務(wù)邏輯給出對應(yīng)的響應(yīng)結(jié)果。
6、用戶取得結(jié)果

如下如所示:

來看一下 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

token 分成了三部分,頭部(header),荷載(Payload) 和 簽名(Signature),每部分用 . 分隔,其中頭部和荷載使用了base64編碼,分別解碼之后得到兩個JSON串:

第一部分-頭部:

{"alg": "HS256","typ": "JWT" }

alg字段為加密算法,這是告訴我們 HMAC 采用 HS512 算法對 JWT 進(jìn)行的簽名。

第二部分-荷載:

{"sub": "1234567890","name": "John Doe","iat": 1516239022 }

荷載的字段及含義:

  • iss: 該JWT的簽發(fā)者
  • sub: 該JWT所面向的用戶
  • aud: 接收該JWT的一方
  • exp(expires): 什么時候過期,這里是一個Unix時間戳
  • iat(issued at): 在什么時候簽發(fā)的

這段告訴我們這個Token中含有的數(shù)據(jù)聲明(Claim),這個例子里面有三個聲明:sub, name 和 iat。在我們這個例子中,分別代表著
所面向的用戶、用戶名、創(chuàng)建時間,當(dāng)然你可以把任意數(shù)據(jù)聲明在這里。

第三部分-簽名:

第三部分簽名則不能使用base64解碼出來,該部分用于驗證頭部和荷載數(shù)據(jù)的完整性。

JWT的生成和解析

引入依賴:

<!-- JWT --> <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version> </dependency>

創(chuàng)建一個測試類嘗試一下 JWT 的生成:

public class Test {public static void main(String[] args){String token = Jwts.builder()主題 放入用戶名.setSubject("niceyoo")自定義屬性 放入用戶擁有請求權(quán)限.claim("authorities","admin")失效時間.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))簽名算法和密鑰.signWith(SignatureAlgorithm.HS512, "tmax").compact();System.out.println(token);}}

控制臺打印如下:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1ODM1M30.keCiHrcEr0IWXfZLocgHS8znn7uSiaZW1IT6bTs-EQG0NPsb6-Aw_XbGQea4mez2CcAflgMqtzIpsDjZsUOVug

數(shù)據(jù)聲明(Claim)是一個自定義屬性,可以用來放入用戶擁有請求權(quán)限。上邊為簡單直接傳了一個 ‘a(chǎn)dmin’。

再看看解析:

public static void main(String[] args){try {解析tokenClaims claims = Jwts.parser().setSigningKey("tmax").parseClaimsJws("eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1OTc2Mn0.MkSJtGaVePLa-eM3gylh1T3fwODg-6ceDDOxscXAQKun-qNrbQFcKPNqXhblbXPNLhaJyEnwugNANCTs98UNmA").getBody();System.out.println(claims);獲取用戶名String username = claims.getSubject();System.out.println("username:"+username);獲取權(quán)限String authority = claims.get("authorities").toString();System.out.println("權(quán)限:"+authority);} catch (ExpiredJwtException e) {System.out.println("jwt異常");} catch (Exception e){System.out.println("異常");} }

控制臺打印:

{sub=niceyoo, authorities=admin, exp=1559459762} username:niceyoo 權(quán)限:admin

JWT 本身沒啥難度,但安全整體是一個比較復(fù)雜的事情,JWT 只不過提供了一種基于 token 的請求驗證機(jī)制。但我們的用戶權(quán)限,對于 API 的權(quán)限劃分、資源的權(quán)限劃分,用戶的驗證等等都不是JWT負(fù)責(zé)的。也就是說,請求驗證后,你是否有權(quán)限看對應(yīng)的內(nèi)容是由你的用戶角色決定的。所接下來才是我們的重點,Spring Security 整合 JWT。

集成JWT

要想要 JW T在 Spring 中工作,我們應(yīng)該新建一個 JWT filter,并把它配置在 WebSecurityConfig 中。

WebSecurityConfigurerAdapter.java

@Slf4j @Configuration @EnableGlobalMethodSecurity(prePostEnabled=true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate AuthenticationSuccessHandler successHandler;@Autowiredprivate AuthenticationFailHandler failHandler;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());加密}@Overrideprotected void configure(HttpSecurity http) throws Exception {ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();registry.and()表單登錄方式.formLogin().permitAll()成功處理類.successHandler(successHandler)失敗.failureHandler(failHandler).and().logout().permitAll().and().authorizeRequests()任何請求.anyRequest()需要身份認(rèn)證.authenticated().and()關(guān)閉跨站請求防護(hù).csrf().disable()前后端分離采用JWT 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()添加JWT過濾器 除已配置的其它請求都需經(jīng)過此過濾器.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));} }

相較于上一篇主要多了如下一行配置:

.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));

JWTAuthenticationFilter.java

@Slf4j public class JWTAuthenticationFilter extends BasicAuthenticationFilter {private Integer tokenExpireTime;public JWTAuthenticationFilter(AuthenticationManager authenticationManager, Integer tokenExpireTime) {super(authenticationManager);this.tokenExpireTime = tokenExpireTime;}public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {super(authenticationManager, authenticationEntryPoint);}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String header = request.getHeader(SecurityConstant.HEADER);if(StrUtil.isBlank(header)){header = request.getParameter(SecurityConstant.HEADER);}Boolean notValid = StrUtil.isBlank(header) || (!header.startsWith(SecurityConstant.TOKEN_SPLIT));if (notValid) {chain.doFilter(request, response);return;}try {UsernamePasswordAuthenticationToken 繼承 AbstractAuthenticationToken 實現(xiàn) Authentication所以當(dāng)在頁面中輸入用戶名和密碼之后首先會進(jìn)入到 UsernamePasswordAuthenticationToken驗證(Authentication),UsernamePasswordAuthenticationToken authentication = getAuthentication(header, response);SecurityContextHolder.getContext().setAuthentication(authentication);}catch (Exception e){e.toString();}chain.doFilter(request, response);}private UsernamePasswordAuthenticationToken getAuthentication(String header, HttpServletResponse response) {用戶名String username = null;權(quán)限List<GrantedAuthority> authorities = new ArrayList<>();try {解析tokenClaims claims = Jwts.parser().setSigningKey(SecurityConstant.JWT_SIGN_KEY).parseClaimsJws(header.replace(SecurityConstant.TOKEN_SPLIT, "")).getBody();logger.info("claims:"+claims);獲取用戶名username = claims.getSubject();logger.info("username:"+username);獲取權(quán)限String authority = claims.get(SecurityConstant.AUTHORITIES).toString();logger.info("authority:"+authority);if(!StringUtils.isEmpty(authority)){authorities.add(new SimpleGrantedAuthority(authority));}} catch (ExpiredJwtException e) {ResponseUtil.out(response, ResponseUtil.resultMap(false,401,"登錄已失效,請重新登錄"));} catch (Exception e){log.error(e.toString());ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"解析token錯誤"));}if(StrUtil.isNotBlank(username)) {踩坑提醒 此處password不能為nullUser principal = new User(username, "", authorities);return new UsernamePasswordAuthenticationToken(principal, null, authorities);}return null;} }

接下來我們啟動項目看看:

訪問項目中已有的鏈接:

http://localhost:7777/tmax/videoCategory/getAll

老樣子認(rèn)證一波:

其中 niceyoo、****** 為數(shù)據(jù)庫用戶信息

登陸成功后獲取返回的 token,注意,此 token 是由 JWT 生成的:

String token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()主題 放入用戶名.setSubject(username)自定義屬性 放入用戶擁有請求權(quán)限.claim(SecurityConstant.AUTHORITIES, authorities)失效時間.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))簽名算法和密鑰.signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY).compact();

瀏覽器返回 token 如下:

然后我們通過 token 憑證去訪問上邊的方法:

后臺打印信息:

claims:{sub=niceyoo, authorities=admin, exp=1559472866} username:niceyoo authority:admin

隨便改一下 token ,返回如下:

總結(jié)

以上是生活随笔為你收集整理的SpringSecurity 整合 JWT的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。