zullフィルタ
18879 ワード
一、機能点
(1)特定のパスを指し示すフィルタリング不要
例えばログインインタフェース、辞書インタフェース、
(2)tokenチェック
redisによってtoken検証を実現し、失効:token検証に失敗した.有効:有効期限の延長
(3)鑑識権
権限ツリーに基づいて要求されたurlが合法かどうかを検証します.
(4)パッケージ要求データ
二、コード
(1)フィルタ
@RefreshScope
public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
@Autowired
private HystrixWrappedAuthServiceClient hystrixWrappedAuthServiceClient;
@Value("${timestamp.validate.limit:180}")
private String validateLimit;
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format(" IP %s %s request to %s", getIpAddr(request), request.getMethod(), request.getRequestURL().toString()));
log.info("url is :" + request.getRequestURL().toString());
if (skipAllFilters(request.getRequestURL().toString())) {
log.info(" ");
return null;
}
String url = request.getRequestURL().toString();
log.info(" .....url:{}",url);
// token , token
if (containUpload(url)) {
String token = request.getParameter("token");
if (!validateToken(url, token)) {
ZuulResponseModel responseModel = new ZuulResponseModel();
log.warn("token ");
responseModel.setRepCode(RespCode.ZUUL_TOKEN_ERROR);
responseModel.setRepMsg("token ");
ctx = getContextForError(ctx, responseModel);
return null;
}
ZuulRequestModel requestModel=new ZuulRequestModel();
requestModel.setUrl(url);
requestModel.setAuthUrl(request.getServletPath());
requestModel.setToken(token);
if (!validateAuth(requestModel)) {
ZuulResponseModel responseModel = new ZuulResponseModel();
log.warn(" ");
responseModel.setRepCode(RespCode.ZUUL_AUTH_ERROR);
responseModel.setRepMsg(RespMsg.ZUUL_AUTH_ERROR_MSG);
ctx = getContextForError(ctx, responseModel);
return null;
}
log.info(" ");
return null;
}
ZuulResponseModel parameterCheckResult = validateWithParameters(request);
if (null != parameterCheckResult) {
if (RespCode.SUCCESS == parameterCheckResult.getRepCode()) {
return null;
} else {
ctx = getContextForError(ctx, parameterCheckResult);
return null;
}
}
// token
String jsonStr = getData(request);
JSONObject jsonData = JSONObject.parseObject(jsonStr);
ZuulRequestModel requestModel;
ZuulRequestArrayModel requestArrayModel = null;
try {
requestModel = JSON.parseObject(jsonData.toString(), ZuulRequestModel.class);
} catch (Exception e) {
requestArrayModel=JSON.parseObject(jsonData.toString(), ZuulRequestArrayModel.class);
requestModel=new ZuulRequestModel();
requestModel.setAuthUrl(requestArrayModel.getAuthUrl());
requestModel.setJsonStr(requestArrayModel.getJsonStr());
requestModel.setSign(requestArrayModel.getSign());
requestModel.setTime(requestArrayModel.getTime());
requestModel.setToken(requestArrayModel.getToken());
requestModel.setUrl(requestArrayModel.getUrl());
requestModel.setUserId(requestArrayModel.getUserId());
}
requestModel.setJsonStr(jsonStr);
requestModel.setUrl(url);
requestModel.setAuthUrl(request.getServletPath());
if (!validateToken(url, requestModel.getToken())) {
ZuulResponseModel responseModel = new ZuulResponseModel();
log.warn("token ");
responseModel.setRepCode(RespCode.ZUUL_TOKEN_ERROR);
responseModel.setRepMsg("token ");
ctx = getContextForError(ctx, responseModel);
return null;
}
log.debug(" token ");
// //TODO , ,
// String[] tokenInfo = requestModel.getToken().split("_");
// if (!Pattern.matches("w.*", tokenInfo[0])) {
// return null;
// }
if (!validateAuth(requestModel)) {
ZuulResponseModel responseModel = new ZuulResponseModel();
log.warn(" ");
responseModel.setRepCode(RespCode.ZUUL_AUTH_ERROR);
responseModel.setRepMsg(RespMsg.ZUUL_AUTH_ERROR_MSG);
ctx = getContextForError(ctx, responseModel);
return null;
}
log.debug(" ");
return null;
}
private boolean containUpload(String url) {
if (Pattern.matches(".*/upload", url)) {
return true;
}
return false;
}
private String getData(HttpServletRequest req) {
String result = null;
try {
BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) req.getInputStream(), "utf-8"));
StringBuffer sb = new StringBuffer("");
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
result = sb.toString();
log.info(" data " + result);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
private boolean validateToken(String url, String token) {
log.info("validateToken ()");
log.info(" : {url:" + url + ",token:" + token + "}");
boolean isValid = false;
if (Pattern.matches(".*/noauth/.*|.*/login/.*", url)) { // token
return true;
}
if (null == token || "".equals(token.trim())) {
log.warn("token is null");
log.info("token is null");
isValid = false;
}
isValid = hystrixWrappedAuthServiceClient.validate(token);
log.warn("token:" + token + "----- isValid :" + isValid);
log.info("validateToken() ,isValid:" + isValid);
return isValid;
}
private RequestContext getContextForError(RequestContext ctx, ZuulResponseModel responseModel) {
RequestContext requestContext = ctx;
ctx.setResponseBody(responseModel.toJsonString());
ctx.setSendZuulResponse(false);
ctx.getResponse().setContentType("text/html;charset=UTF-8");
ctx.getResponse().setContentType(String.valueOf(MediaType.APPLICATION_JSON));
return requestContext;
}
private boolean skipAllFilters(String requestUrl) {
boolean isSkip = false;
if (Pattern.matches(".*/noauth/.*|.*/dict/.*", requestUrl)) {
isSkip = true;
}
return isSkip;
}
@SuppressWarnings("static-access")
private ZuulResponseModel validateWithParameters(HttpServletRequest request) {
if (!Pattern.matches(".*/exportInfoByExcel|.*/exportVehicleByExcel|.*/storagePlan/importTemplateDownload|.*/order/exportInfoByExcel|.*/stock/exportInfoByExcel", request.getRequestURL())) {
return null;
}
ZuulResponseModel responseModel = new ZuulResponseModel();
Map params = request.getParameterMap();
StringBuffer parameterbf = new StringBuffer();
Iterator> paramsEntryI = params.entrySet().iterator();
while (paramsEntryI.hasNext()) {
Entry e = paramsEntryI.next();
String[] values = e.getValue();
for (int i = 0; i < values.length; i++) {
String value = values[i];
parameterbf.append(e.getKey()).append("=").append(value).append("&");
}
}
log.info("--------------parameter in url is: " + parameterbf.toString());
String[] timeValues = params.get("time");
if (null != timeValues && timeValues.length == 1) {
Timestamp nowTime = new Timestamp(System.currentTimeMillis());
Timestamp requestTime = new Timestamp(Long.parseLong(timeValues[0]));
log.info(" :" + Integer.parseInt(validateLimit));
if (Math.abs(nowTime.getTime() / 1000 - requestTime.getTime()) > Integer.parseInt(validateLimit)) {
log.warn(" ");
responseModel.setRepCode(RespCode.ZUUL_TIMESTAMP_ERROR);
responseModel.setRepMsg(RespMsg.ZUUL_TIMESTAMP_ERROR_MSG);
return responseModel;
}
responseModel.setRepCode(RespCode.SUCCESS);
log.debug(" ");
return responseModel;
}
String[] tokens = params.get("token");
String[] signs = params.get("sign");
String baseSignMsg = "time=" + timeValues[0] + "&token=" + tokens[0];
log.info("----------------------------base sign message is:(" + baseSignMsg + ")------");
log.info("----------------------------encrypt sign message is:(" + MD5Util.getInstance().encrypt(baseSignMsg) + ")------");
if (null != signs && 1 <= signs.length) {
log.info("----------------------------received sign message is:(" + signs[0] + ")------");
}
if (null == signs || 1 != signs.length || !MD5Util.getInstance().encrypt(baseSignMsg).equalsIgnoreCase(signs[0])) {
log.warn("sign ");
responseModel.setRepCode(RespCode.ZUUL_SIGN_ERROR);
responseModel.setRepMsg("");
return responseModel;
}
log.info("----------------------------sign is valid!------");
return null;
}
//
private boolean validateAuth(ZuulRequestModel requestModel) {
boolean isValid = false;
if (Pattern.matches(".*/noauth/.*", requestModel.getUrl())) {
return true;
}
log.info(requestModel.getUserId());
log.info(requestModel.getAuthUrl());
String userId = requestModel.getUserId();
String authUrl = requestModel.getAuthUrl();
log.info(" ......authUrl={},userId={}",authUrl,userId);
if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(authUrl)) {
return isValid;
}
return hystrixWrappedAuthServiceClient.validateByUrl(Long.valueOf(userId), authUrl);
}
(2)token検証実装
token検証ロジックは別の権限サービスにあるため、サービス間呼び出しが必要です.
@RequestMapping("/token/validate")
public boolean validateToken(@RequestParam("token") String token) {
return userService.validateToken(token);
}
@Override
public boolean validateToken(String token) {
TokenEntity tokenModel = tokenManager.getToken(token);
logger.info("validateToken(){}=====tokenModel:" + tokenModel);
boolean isValidate = tokenManager.checkToken(tokenModel);
logger.info("validateToken(){}=====isValidate:" + isValidate);
return isValidate;
}
tokenエンティティ、key指定文字列+userId
public class TokenEntity {
private static final String LOGIN_TOKEN_PREFIX = "LOGIN_TOKEN_USER_";
// id
private Long userId;
// uuid
private String token;
// redis key
private String redisKey;
public TokenEntity(Long userId, String token) {
this.userId = userId;
this.token = token;
this.redisKey = LOGIN_TOKEN_PREFIX + userId;
}
/**
* Getter method for property userId.
*
* @return property value of userId
*/
public Long getUserId() {
return userId;
}
/**
* Setter method for property userId.
*
* @param userId value to be assigned to property userId
*/
public void setUserId(Long userId) {
this.userId = userId;
}
/**
* Getter method for property token.
*
* @return property value of token
*/
public String getToken() {
return token;
}
/**
* Setter method for property token.
*
* @param token value to be assigned to property token
*/
public void setToken(String token) {
this.token = token;
}
/**
* Getter method for property redisKey.
*
* @return property value of redisKey
*/
public String getRedisKey() {
return redisKey;
}
/**
* Setter method for property redisKey.
*
* @param redisKey value to be assigned to property redisKey
*/
public void setRedisKey(String redisKey) {
this.redisKey = redisKey;
}
}
token実装クラス作成、検証、取得、削除
@Service
public class RedisTokenManager implements TokenManager {
private static Logger logger = LoggerFactory.getLogger(RedisTokenManager.class);
@Autowired
private RedisUtils redisUtils;
@Value("${token.timeout.second:1800}")
private String timeout;
/**
* @param userId
* @return
* @see com.anji.allways.business.user.service.TokenManager#createToken(java.lang.Long)
*/
@Override
public TokenEntity createToken(Long userId, String userName) {
logger.info("createToken ");
logger.info(" :userId{}" + userId + ",userName{}" + userName);
// uuid token
String token = userName + "_" + userId + "_" + UUIDUtil.getInstance().getUUID32();
TokenEntity tokenModel = new TokenEntity(userId, token);
// redis
logger.info("tokenModel{}" + tokenModel.toString());
redisUtils.set(tokenModel.getRedisKey(), tokenModel.getToken(), Integer.parseInt(timeout));
logger.info("createToken ");
return tokenModel;
}
/**
* @param token
* @return
* @see com.anji.allways.business.user.service.TokenManager#checkToken(com.anji.allways.business.user.entity.TokenEntity)
*/
@Override
public boolean checkToken(TokenEntity tokenModel) {
logger.info("checkTokne ");
if (tokenModel == null) {
return false;
}
logger.info("tokenModel{}" + tokenModel.toString());
String token = redisUtils.get(tokenModel.getRedisKey());
logger.info("token{}" + token);
if (token == null || !token.equals(tokenModel.getToken())) {
return false;
}
// , , token
logger.info("key{}" + tokenModel.getRedisKey() + ",value:{}" + redisUtils.get(tokenModel.getRedisKey()));
redisUtils.set(tokenModel.getRedisKey(), redisUtils.get(tokenModel.getRedisKey()),
Integer.parseInt(timeout));
logger.info("checkTokne ");
return true;
}
/**
* @param authentication
* @param platform
* @return
* @see com.anji.allways.business.user.service.TokenManager#getToken(java.lang.String)
*/
@Override
public TokenEntity getToken(String authentication) {
if (null == authentication) {
return null;
}
String[] param = authentication.split("_");
if (param.length != 5 && param.length != 3) {
return null;
}
// userId token token,
Long userId = Long.parseLong(param[1]);
String token = authentication;
return new TokenEntity(userId, token);
}
/**
* @param userId
* @see com.anji.allways.business.user.service.TokenManager#deleteToken(java.lang.Long)
*/
@Override
public void deleteToken(TokenEntity tokenModel) {
redisUtils.del(tokenModel.getRedisKey());
}
/**
* @param userVO
* @return
* @see com.anji.allways.business.auth.service.TokenManager#createToken(com.anji.allways.business.auth.vo.UserVO)
*/
@Override
public TokenEntity createToken(UserVO userVO) {
logger.info("createToken ");
// uuid token
String token = userVO.getUserName() + "_" + userVO.getId() + "_"+ userVO.getUserType() +"_"+ userVO.getBelongTo() +"_" +UUIDUtil.getInstance().getUUID32();
TokenEntity tokenModel = new TokenEntity(userVO.getId(), token);
logger.info("tokenModel{}",tokenModel.toString());
// redis
redisUtils.set(tokenModel.getRedisKey(), tokenModel.getToken(), Integer.parseInt(timeout));
logger.info("createToken: ");
return tokenModel;
}
}
(3)認証の実現
@RequestMapping("/validate/url")
public boolean validateByUrl(Long userId, String authUrl) {
logger.info("userId={},authUrl={}",userId,authUrl);
return authService.queryAuthByUrl(userId,authUrl);
}
@Service
@Transactional
public class AuthServiceImpl extends AbstractService implements AuthService {
@Autowired
private AuthMapper authMapper;
@Override
public CommonMapper getCommonMapper() {
return authMapper;
}
@Override
public boolean queryAuthByUrl(Long userId, String url) {
List auths = authMapper.queryAuthByUrl(userId);
if(CollectionUtils.isEmpty(auths)) {
return false;
}
for (AuthEntity authEntity : auths) {
if(url.contains(authEntity.getUrl())) {
return true;
}
}
return false;
}
}
ユーザー権限sqlの取得