SpringCloud利用閘道器攔截做Token驗證(JWT方式) SpringCloud利用閘道器攔截做Token驗證(JWT方式)
阿新 • • 發佈:2018-11-08
SpringCloud利用閘道器攔截做Token驗證(JWT方式)
2018年09月29日 15:51:50 19八9 閱讀數:23 更多<div class="tags-box space"> <span class="label">個人分類:</span> <a class="tag-link" href="https://blog.csdn.net/qq_34707991/article/category/6696592" target="_blank">常見程式設計方法 </a> </div> </div> <div class="operating"> </div> </div> </div> </div> <article> <div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post"> <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-bb1edad192.css"> <div class="htmledit_views"> <p>背景:前後分離</p>
關於JWT的內容,請看以下連結,主要看它的原理,以及缺點!
https://blog.csdn.net/memmsc/article/details/78122931
步驟1:前端傳userName+password到後端,後端為springcloud架構,經過閘道器的攔截器攔截請求,攔截器在專案啟動的時候@Component進行載入。
步驟2:如果是第一次登陸,放行,進入JWT的加密生成Token階段(還可以寫入登陸使用者的其他資訊放在JWTmap中,之後可以利用Token來獲取該使用者資訊),加密token需要一個隨機數作為加密欄位,將token的失效時間設定為一天,並且放到reids裡面,設定該redis裡面的token過期時間為30分鐘,最後將Token返回給前端。
步驟3:以後任何的請求都帶Token到後端去請求。
步驟4:攔截到非登陸請求,進行解密,鑑權,如果鑑權通過,更新redis裡面token欄位的失效時間,如果還有5分鐘失效,再設定還有30分鐘,目的就是讓密碼的過期時間變的活躍。
大致就是以上的過程,核心程式碼主要在閘道器攔截器解密鑑權和登陸介面的加密兩部分
0,controller層的將得到的token做儲存redis和設定過期時間的操作
-
compactJws = authService.generateJwt(username, password, userBean);
-
//將token存在redis裡
-
stringRedisTemplate.opsForValue().
set(
"token", compactJws);
-
//設定redis裡面的資料失效時間為半小時
-
stringRedisTemplate.expire(
"token",
1800,
TimeUnit.
SECONDS);
1,登陸介面的加密:
-
package com.movitech.user.service.imp;
-
-
import com.movitech.commons.entity.UserBean;
-
import com.movitech.commons.utils.CommonConstants;
-
import com.movitech.user.service.AuthService;
-
import io.jsonwebtoken.Jwts;
-
import io.jsonwebtoken.SignatureAlgorithm;
-
import org.joda.time.DateTime;
-
import org.springframework.stereotype.Service;
-
-
import java.util.Base64;
-
import java.util.HashMap;
-
import java.util.Map;
-
-
/**
-
* 使用者身份驗證Service
-
*/
-
@
Service(value =
"authService")
-
public
class AuthServiceImpl implements AuthService {
-
@
Override
-
public
String generateJwt(
String userName,
String userPassword,
UserBean userBean) {
-
// Base64編碼後的secretKey
-
byte[] secretKey =
Base64.getEncoder().encode(
CommonConstants.
SECURITY_KEY.getBytes());
-
// 設定失效時間
-
DateTime expirationDate = new
DateTime().plusDays(
1);
-
//DateTime expirationDate = new DateTime().plusMinutes(30);
-
// Claims是需要儲存到token中的資訊,可以自定義,需要存什麼就放什麼,會儲存到token的payload中
-
Map<
String,
Object> claims = new
HashMap<>();
-
// 使用者角色
-
claims.put(
"role",
"user");
-
// 使用者名稱
-
claims.put(
"userName", userName);
-
claims.put(
CommonConstants.
USER_ID, userBean.getId());
-
claims.put(
"uuid",
UUID.randomUUID().
toString());
-
String compactJws =
Jwts.builder()
-
// 設定subject,一般是使用者的唯一標識,比如使用者物件的ID,使用者名稱等,目前設定的是userCode
-
.setSubject(userName)
-
// 設定失效時間
-
.setExpiration(expirationDate.toDate())
-
.addClaims(claims)
-
// 加密演算法是HS512,加密解密統一就可以
-
.signWith(
SignatureAlgorithm.
HS512, secretKey)
-
.compact();
-
return compactJws;
-
}
-
-
}
以上常量類和pojo此處省略。。。。
2,閘道器攔截器解密鑑權:
-
package com.movitech.gateway.filter;
-
-
import com.movitech.commons.dto.ErrorResponseMap;
-
import com.movitech.commons.enums.ErrorCode;
-
import com.movitech.commons.utils.CommonConstants;
-
import com.movitech.commons.utils.JsonUtil;
-
import com.movitech.commons.utils.ResponseUtil;
-
import com.netflix.zuul.ZuulFilter;
-
import com.netflix.zuul.context.RequestContext;
-
import io.jsonwebtoken.*;
-
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
-
import org.springframework.http.HttpHeaders;
-
import org.springframework.http.HttpMethod;
-
import org.springframework.stereotype.Component;
-
import org.springframework.util.StringUtils;
-
-
import javax.servlet.http.HttpServletRequest;
-
import java.util.Base64;
-
-
@Component
-
public
class SecurityFilter extends ZuulFilter {
-
@Override
-
public String filterType() {
-
return FilterConstants.PRE_TYPE;
-
}
-
-
@Override
-
public int filterOrder() {
-
return FilterConstants.PRE_DECORATION_FILTER_ORDER -
1;
-
}
-
-
@Override
-
public boolean shouldFilter() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
HttpServletRequest request = ctx.getRequest();
-
if (request.getRequestURL().toString().contains(
"loginInfo") || request.getRequestURL().toString().contains(
"info")) {
-
return
false;
-
}
-
// TODO
-
return
true;
-
}
-
-
@Override
-
public Object run() {
-
RequestContext ctx = RequestContext.getCurrentContext();
-
HttpServletRequest request = ctx.getRequest();
-
final String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
-
-
if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
-
return
null;
-
}
else {
-
if (StringUtils.isEmpty(authorizationHeader) || !authorizationHeader.startsWith(CommonConstants.BEARER)) {
-
// Missing or invalid Authorization header
-
ErrorResponseMap errorResponseMap = ResponseUtil.createErrorResponse(
null,
"Missing or invalid Authorization header!",
-
ErrorCode.INVALID_AUTHORIZATION_HEADER, request,
null);
-
denyAccess(ctx,errorResponseMap);
-
return JsonUtil.serializeToString(errorResponseMap);
-
}
-
final String token = authorizationHeader.substring(
7);
-
try {
-
byte[] secretKey = Base64.getEncoder().encode(CommonConstants.SECURITY_KEY.getBytes());
-
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
-
if (claims !=
null) {
-
//獲取redis裡面資料的存活時間
-
Long expirationDate = stringRedisTemplate.getExpire(
"token",TimeUnit.SECONDS);
-
//如果還剩餘5分鐘,重置redis裡面資料的存活時間
-
if(expirationDate >
300){
-
stringRedisTemplate.expire(
"token",
1800,TimeUnit.SECONDS);
-
}
else {
-
ErrorResponseMap errorResponseMap = new ErrorResponseMap();
-
Error error = new Error(
null,
"Token expired!",
"",
1003,
"");
-
errorResponseMap.setSuccess(
false);
-
errorResponseMap.setMessage(
null);
-
errorResponseMap.setError(error);
-
denyAccess(ctx,errorResponseMap);
-
return JsonUtil.serializeToString(errorResponseMap);
-
}
-
String userName = (String) claims.
get(CommonConstants.USER_CODE);
-
Integer userId = (Integer) claims.
get(CommonConstants.USER_ID);
-
ctx.addZuulRequestHeader(CommonConstants.USER_CODE, userName);
-
ctx.addZuulRequestHeader(CommonConstants.USER_ID, String.valueOf(userId));
-
}
-
}
catch (MalformedJwtException ex) {
-
ErrorResponseMap errorResponseMap = ResponseUtil.createErrorResponse(
null,
"Invalid token!",
-
ErrorCode.INVALID_AUTHORIZATION_HEADER, request, ex);
-
denyAccess(ctx,errorResponseMap);
-
return JsonUtil.serializeToString(errorResponseMap);
-
}
catch (SignatureException ex) {
-
ErrorResponseMap errorResponseMap = ResponseUtil.createErrorResponse(
null,
"Token Signature error!",
-
ErrorCode.SIGNATURE_EXCEPTION, request, ex);
-
denyAccess(ctx,errorResponseMap);
-
return JsonUtil.serializeToString(errorResponseMap);
-
}
catch (ExpiredJwtException ex) {
-
ErrorResponseMap errorResponseMap = ResponseUtil.createErrorResponse(
null,
"Token expired!",
-
ErrorCode.EXPIRED_JWT_EXCEPTION, request, ex);
-
denyAccess(ctx,errorResponseMap);
-
return JsonUtil.serializeToString(errorResponseMap);
-
}
-
}
-
return
null;
-
}
-
-
private void denyAccess(RequestContext ctx, ErrorResponseMap authResult) {
-
String result = JsonUtil.serializeToString(authResult);
-
ctx.setSendZuulResponse(
false);
-
ctx.setResponseStatusCode(
401);
-
try {
-
ctx.getResponse().getWriter().write(result);
-
}
catch (Exception e){}
-
}
-
}
以上程式碼是核心程式碼,下面是自己專案中涉及到的異常包裝類,以及util類,可以不管下面的,直接去封裝
以上涉及到的類:
(1)ErrorResponseMap
-
package
com
.movitech
.commons
.dto;
-
-
import
com
.fasterxml
.jackson
.annotation
.JsonProperty;
-
import
com
.movitech
.commons
.exception
.Error;
-
import
lombok
.Getter;
-
import
lombok
.Setter;
-
-
@
Getter
-
@Setter
-
public class ErrorResponseMap extends ResponseMap {
-
@
JsonProperty(
value =
"error")
-
private Error error;
-
@
JsonProperty(
value =
"stackTrace")
-
private String stackTrace;
-
}
(1.1)Error
-
package com.movitech.commons.exception;
-
-
import lombok.AllArgsConstructor;
-
import lombok.Getter;
-
import lombok.NoArgsConstructor;
-
import lombok.Setter;
-
-
@Getter
-
@Setter
-
@NoArgsConstructor
-
@AllArgsConstructor
-
public
class Error {
-
// 標準的 Http status code
-
private Integer httpStatusCode;
-
// 自定義的錯誤說明
-
private String errorMsg;
-
// 異常資訊
-
private String exceptionMsg;
-
// 自定義的錯誤程式碼
-
private Integer errorCode;
-
// 異常的類名
-
private String exceptionClassName;
-
}
(2)ErrorCode
-
package com.movitech.commons.enums;
-
-
/**
-
* 自定義的錯誤程式碼的列舉
-
*/
-
public
enum ErrorCode {
-
// Token 簽名錯誤
-
SIGNATURE_EXCEPTION(
1000),
-
// Token 過期
-
EXPIRED_JWT_EXCEPTION(
1001),
-
// 無效的Authorization header
-
INVALID_AUTHORIZATION_HEADER(
1002);
-
private Integer errorCode;
-
ErrorCode(Integer errorCode) {
-
this.errorCode = errorCode;
-
}
-
-
@Override
-
public String toString() {
-
return errorCode.toString();
-