SpringBoot項目引入token設(shè)置方式
一. 先了解熟悉JWT(JSON Web Token)
看這些介紹、結(jié)構(gòu)之類的,確實挺無聊的;想直接進(jìn)入主題的話,就跳過第一大步。
望各位同仁給出相關(guān)意見,以備我來更加深入的學(xué)習(xí)。
1. JSON Web Token是什么鬼?
這個東西,反正理解成一個標(biāo)準(zhǔn)就行了,啥標(biāo)準(zhǔn)我也不知道。反正就是用于各種信息的安全性傳輸。
2. JSON Web令牌應(yīng)用的場景
1.授權(quán),在用戶登錄后會給用戶一個token,在用戶后續(xù)的所有請求后臺資源的操作都將攜帶這個token,只有被token允許的操作才能執(zhí)行。
2.信息交換,應(yīng)用于各種數(shù)據(jù)信息交換的場景;目前這種場景我也沒有涉及到過,嘿嘿!
3. JSON Web令牌結(jié)構(gòu)
JSON Web Tokens由dot(.)分隔的三個部分組成:Header、Payload、Signature;
因此token的形式是:
xxxxx.yyyyy.zzzzz
3.1 Header
標(biāo)頭通常由兩部分組成:令牌的類型,即JWT,以及正在使用的簽名算法,例如HMAC SHA256或RSA。
{ "alg": "HS256", "typ": "JWT" }
然后,這個JSON被編碼為Base64Url,形成JWT的第一部分
3.2 Payload
這一部分是聲明,有三種類型:注冊,公開和私有聲明;
- 注冊
這些是一組預(yù)定義聲明,不是強(qiáng)制性的,但建議使用,以提供一組有用的,可互操作的聲明。其中一些是:iss (issuer), exp (expiration time), sub (subject), aud(audience)等
注:請注意,聲明名稱只有三個字符,因為JWT意味著緊湊。 - 公開
這些可以由使用JWT的人隨意定義。但為避免沖突,應(yīng)在 IANA JSON Web令牌注冊表中定義它們,或者將其定義為包含防沖突命名空間的URI - 私有
私有聲明是提供者和消費(fèi)者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息
payload示例:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
然后,payload經(jīng)過Base64Url編碼,形成JSON Web令牌的第二部分
請注意:對于簽名令牌,此信息雖然可以防止被篡改,但任何人都可以讀取。除非加密,否則不要將秘密信息放在JWT的payload或header中。
3.3 Signature
要創(chuàng)建簽名部分,必須采用 header, payload, secret,標(biāo)頭中指定的算法,并對其進(jìn)行簽名。
例如,如果要使用HMAC SHA256算法,將按以下方式創(chuàng)建簽名:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
3.4 token
最后輸出是三個由點分隔的Base64-URL字符串
4. JSON Web令牌工作原理
- 用戶登陸的時候使用用戶名和密碼發(fā)送POST請求
- 服務(wù)器使用私鑰創(chuàng)建一個jwt
- 服務(wù)器返回這個jwt到瀏覽器
- 瀏覽器將該jwt串加入請求頭中向服務(wù)器發(fā)送請求
- 服務(wù)器驗證jwt
- 返回相應(yīng)的資源給客戶端
二. 動手開始搞
1.引入依賴
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
2. 定義注解
package com.example.fighting.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class UserToken { // 跳過驗證 @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface PassToken { boolean required() default true; } // 需要驗證 @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface UserLoginToken { boolean required() default true; } }
3.新增TokenService接口類和TokenServiceImpl實現(xiàn)類
package com.example.fighting.serviceImpl; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.example.fighting.bean.UserTest; import com.example.fighting.service.TokenService; import org.springframework.stereotype.Service; @Service public class TokenServiceImpl implements TokenService { @Override public String getToken(UserTest userTest) { String token=""; token= JWT.create().withAudience(userTest.getId().toString()) .sign(Algorithm.HMAC256(userTest.getPassword())); return token; } }
4.新增攔截器
package com.example.fighting.config; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.example.fighting.bean.UserTest; import com.example.fighting.service.UserTestService; import jdk.nashorn.internal.ir.annotations.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired // @Reference UserTestService userTestService; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 從 http 請求頭中取出 token // 如果不是映射到方法直接通過 if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //檢查是否有passtoken注釋,有則跳過認(rèn)證 if (method.isAnnotationPresent(UserToken.PassToken.class)) { UserToken.PassToken passToken = method.getAnnotation(UserToken.PassToken.class); if (passToken.required()) { return true; } } //檢查有沒有需要用戶權(quán)限的注解 if (method.isAnnotationPresent(UserToken.UserLoginToken.class)) { UserToken.UserLoginToken userLoginToken = method.getAnnotation(UserToken.UserLoginToken.class); if (userLoginToken.required()) { // 執(zhí)行認(rèn)證 if (token == null) { throw new RuntimeException("無token,請重新登錄"); } // 獲取 token 中的 user id String userId; try { userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("401"); } UserTest user = userTestService.findUserTestById(Long.parseLong(userId)); if (user == null) { throw new RuntimeException("用戶不存在,請重新登錄"); } // 驗證 token JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new RuntimeException("401"); } return true; } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
5.添加攔截器配置
package com.example.fighting.interceptor; import com.example.fighting.config.AuthenticationInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns("/**"); } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } }
6. 在controller中添加登錄接口
//登錄 @ApiOperation(value="登錄接口",notes ="驗證登錄后獲取一個token") @ApiImplicitParams({ @ApiImplicitParam(paramType="query",name="username",value="賬號",required = true), @ApiImplicitParam(paramType="query",name="password",value="密碼",required = true) }) @PostMapping("/login") @ResponseBody public Map login(UserTest user) { Map<Object, Object> map = new HashMap<>(); UserTest userForBase = userTestService.findUserTestByUserName(user.getUsername()); if (userForBase == null) { map.put("message", "登錄失敗,用戶不存在"); return map; } else { if (!userForBase.getPassword().equals(user.getPassword())) { map.put("message", "登錄失敗,密碼錯誤"); return map; } else { String token = tokenService.getToken(userForBase); map.put("token", token); map.put("user", userForBase); return map; } } } @ApiOperation(value="測試token",notes ="測試token是否通過") @UserToken.UserLoginToken @GetMapping("/getMessage") public String getMessage() { return "你已通過驗證"; }
7.接口測試
1.用postman不加token訪問
調(diào)接口:http://localhost:8014/userTest/getMessage
后臺打印結(jié)果:
2.用postman添加錯誤的token訪問
打印結(jié)果:
3.正常登錄,使用登錄后返回的token
登錄接口:http://localhost:8014/userTest/login
調(diào)接口測試token是否生效:
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot中使用Servlet的兩種方式小結(jié)
這篇文章主要介紹了SpringBoot中使用Servlet的兩種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07Java實戰(zhàn)之網(wǎng)上書店管理系統(tǒng)的實現(xiàn)
本文將利用Java語言實現(xiàn)網(wǎng)上書店管理系統(tǒng)。其功能一般包括:圖書信息管理、用戶信息管理、圖書購買、圖書訂單查看、圖書添加、圖書維護(hù)等等,感興趣的可以了解一下2022-06-06springboot動態(tài)注入配置與docker設(shè)置環(huán)境變量的方法
這篇文章主要介紹了springboot動態(tài)注入配置與docker設(shè)置環(huán)境變量的方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04java線程并發(fā)cyclicbarrier類使用示例
CyclicBarrier類似于CountDownLatch也是個計數(shù)器,不同的是CyclicBarrier數(shù)的是調(diào)用了CyclicBarrier.await()進(jìn)入等待的線程數(shù),當(dāng)線程數(shù)達(dá)到了CyclicBarrier初始時規(guī)定的數(shù)目時,所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù),下面使用示例學(xué)習(xí)他的使用方法2014-01-01Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式
這篇文章主要介紹了Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06IntelliJ Idea 2017注冊碼免費(fèi)激活方法
IDEA 全稱 IntelliJ IDEA,是Java語言開發(fā)的集成環(huán)境,IntelliJ在業(yè)界被公認(rèn)為最好的java開發(fā)工具之一。下面給大家介紹IntelliJ Idea 2017注冊碼免費(fèi)激活方法,需要的朋友參考下2018-01-01SpringMVC中的ResourceUrlProviderExposingInterceptor詳解
這篇文章主要介紹了SpringMVC中的ResourceUrlProviderExposingInterceptor詳解,ResourceUrlProviderExposingInterceptor是Spring MVC的一個HandlerInterceptor,用于向請求添加一個屬性,需要的朋友可以參考下2023-12-12