springboot+jwt實現token登陸許可權認證
一 前言
此篇文章的內容也是學習不久,終於到週末有時間碼一篇文章分享知識追尋者的粉絲們,學完本篇文章,讀者將對token類的登陸認證流程有個全面的瞭解,可以動態搭建自己的登陸認證過程;對小專案而已是個輕量級的認證機制,符合開發需求;更多精彩原創內容關注公主號知識追尋者,讀者的肯定,就是對作者的創作的最大支援;
二 jwt實現登陸認證流程
- 使用者使用賬號和麵發出post請求
- 伺服器接受到請求後使用私鑰建立一個jwt,這邊會生成token
- 伺服器返回這個jwt給瀏覽器
- 瀏覽器需要將帶有token的jwt放入請求頭
- 每次手到客戶端請求,伺服器驗證該jwt的token
- 驗證成功返回響應的資源給瀏覽器。否則異常處理
三 相關介紹jwt
3.1jwt 組成
JWT的token由三段資訊構成的,將這三段資訊文字用.
連線一起就構成了JWT字串;
- Header 頭部(包含了令牌的元資料,並且包含簽名和或加密演算法的型別)
- Payload 負載
- Signature 簽名/簽證
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg
3.2 jwt優點
- 簡潔(Compact): 可以通過
URL
,POST
引數或者在HTTP header
- 自包含(Self-contained):負載中包含了所有使用者所需要的資訊,避免多次查詢資料庫
- .因為
Token
是以JSON
加密的形式儲存在客戶端的,所以JWT
是跨語言支援; - 不需要在服務端儲存會話資訊,適用於分散式與微服務;
四 jwt使用者登陸發放token
4.1 pom.xml
專案構件如下
- springboot 2.1;
- jwt 3.4.0;
- maven 3.5
- jdk1.8
- postman介面測試
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
4.2jwt工具類
jwt工具類中有三個方法,分別是生成數字簽名用於使用者首次登陸時傳送jwt給客戶端;其次是校驗方法,用於攔截器攔截所有規則內的url,每個請求都必須帶有伺服器傳送的jwt,經過驗證後才放行請求;最後一個獲得使用者名稱的方法用於查詢金鑰,在驗證jwt時作為引數傳入;
/* *
* @Author lsc
* <p> JWT工具類 </p>
* @Param
* @Return
*/
public class JwtUtil {
// Token過期時間30分鐘
public static final long EXPIRE_TIME = 30 * 60 * 1000;
/* *
* @Author lsc
* <p> 校驗token是否正確 </p>
* @Param token
* @Param username
* @Param secret
* @Return boolean
*/
public static boolean verify(String token, String username, String secret) {
try {
// 設定加密演算法
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
// 效驗TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/* *
* @Author lsc
* <p>生成簽名,30min後過期 </p>
* @Param [username, secret]
* @Return java.lang.String
*/
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附帶username資訊
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
}
/* *
* @Author lsc
* <p> 獲得使用者名稱 </p>
* @Param [request]
* @Return java.lang.String
*/
public static String getUserNameByToken(HttpServletRequest request) {
String token = request.getHeader("token");
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username")
.asString();
}
}
4.3 使用者實體
實體中包含使用者名稱,和密碼,一切從簡;
/**
* @Author lsc
* <p> </p>
*/
@Data
public class SysUser {
private String username;
private String password;
}
4.4Controller
表現層程式碼使用者使用者登陸認證,認證成功後發放token給客戶端;
/**
* @Author lsc
* <p> </p>
*/
@RestController
public class SysUserController {
@PostMapping(value = "/login")
public Map<String, Object> login(@RequestBody SysUser sysUser){
Map<String, Object> map = new HashMap<>();
String username = sysUser.getUsername();
String password = sysUser.getPassword();
// 省略 賬號密碼驗證
// 驗證成功後傳送token
String token = JwtUtil.sign(username,password);
if (token != null){
map.put("code", "200");
map.put("message","認證成功");
map.put("token", token);
return map;
}
map.put("code", "403");
map.put("message","認證失敗");
return map;
}
}
4.5 測試
測試url http://localhost:8080/login
postman post請求測試引數如下
{
"username": "zszxz",
"password": "zszxz"
}
返回內容如下
{
"code": "200",
"message": "認證成功",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODI4OTc4NDUsInVzZXJuYW1lIjoienN6eHoifQ.vyiExkFWCCmQA3PFYL0jJfIiYGWubngqB0WcgmtHOxg"
}
五 jwt登陸攔截認證
基於前面已經實現jwt登入認證後發放token給客戶端;本節內容就是將token放入請求頭中傳送請求給伺服器;伺服器使用攔截器攔截請求對token進行驗證;驗證成功請求通過,否則請求資源失敗;
5.1自定義攔截器
自定義攔截器JwtInterceptor,實現HandlerInterceptor介面,每次請求到達之前都會驗證token是否有效;
/**
* @Author lsc
* <p>token驗證攔截器 </p>
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
SysUserService sysUserService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 從 http 請求頭中取出 token
String token = request.getHeader("token");
// 如果不是對映到方法直接通過
if(!(handler instanceof HandlerMethod)){
return true;
}
if (token != null){
String username = JwtUtil.getUserNameByToken(request);
// 這邊拿到的 使用者名稱 應該去資料庫查詢獲得密碼,簡略,步驟在service直接獲取密碼
boolean result = JwtUtil.verify(token,username,sysUserService.getPassword());
if(result){
System.out.println("通過攔截器");
return true;
}
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
5.2 service
/**
* @Author lsc
* <p> 模擬查詢資料庫獲得賬號密碼 </p>
*/
@Service
public class SysUserService {
public String getPassword(){
return "zszxz";
}
}
5.3攔截器配置
攔截器配置中主要定義攔截請求規則,將攔截器注入WebMvcConfigurer;cors跨域處理;
/* *
* @Author lsc
* <p>攔截器配置 </p>
* @Param
* @Return
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/* *
* @Author lsc
* <p> 設定攔截路徑 </p>
* @Param [registry]
* @Return void
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
/* *
* @Author lsc
* <p> 將攔截器注入context </p>
* @Param []
* @Return com.zszxz.jwt.interceptor.JwtInterceptor
*/
@Bean
public JwtInterceptor authenticationInterceptor() {
return new JwtInterceptor();
}
/* *
* @Author lsc
* <p>跨域支援 </p>
* @Param [registry]
* @Return void
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
.maxAge(3600 * 24);
}
}
5.4Controller
表現層介面用於攔截親求測試
/**
* @Author lsc
* <p> </p>
*/
@RestController
public class TestController {
@GetMapping(value = "/api/test")
public String get(){
return "zszxz";
}
}
5.5 測試
測試url http://localhost:8080/api/test
傳送get請求給伺服器,帶有請求頭,key為token,value為使用者首次登陸時返回的token串;
測試返回內容如下
zszxz
六 官網連結
https://jwt.io/introduction/
原始碼 關注公主號或者作者專欄說明即可獲得