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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringSecurity 整合 JWT

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

項目集成Spring Security(一)

在上一篇基礎上繼續集成 JWT ,實現用戶身份驗證。

前言

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

什么是JWT?

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

JWT的工作流程

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

如下如所示:

來看一下 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

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

第一部分-頭部:

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

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

第二部分-荷載:

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

荷載的字段及含義:

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

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

第三部分-簽名:

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

JWT的生成和解析

引入依賴:

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

創建一個測試類嘗試一下 JWT 的生成:

public class Test {public static void main(String[] args){String token = Jwts.builder()主題 放入用戶名.setSubject("niceyoo")自定義屬性 放入用戶擁有請求權限.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

數據聲明(Claim)是一個自定義屬性,可以用來放入用戶擁有請求權限。上邊為簡單直接傳了一個 ‘admin’。

再看看解析:

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);獲取權限String authority = claims.get("authorities").toString();System.out.println("權限:"+authority);} catch (ExpiredJwtException e) {System.out.println("jwt異常");} catch (Exception e){System.out.println("異常");} }

控制臺打印:

{sub=niceyoo, authorities=admin, exp=1559459762} username:niceyoo 權限:admin

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

集成JWT

要想要 JW T在 Spring 中工作,我們應該新建一個 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()需要身份認證.authenticated().and()關閉跨站請求防護.csrf().disable()前后端分離采用JWT 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()添加JWT過濾器 除已配置的其它請求都需經過此過濾器.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 實現 Authentication所以當在頁面中輸入用戶名和密碼之后首先會進入到 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;權限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);獲取權限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

老樣子認證一波:

其中 niceyoo、****** 為數據庫用戶信息

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

String token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()主題 放入用戶名.setSubject(username)自定義屬性 放入用戶擁有請求權限.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 ,返回如下:

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。