package cn.huge.gateway.filter; import cn.huge.gateway.utils.SpringContextUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @title: 网关拦截器 * @description: 网关拦截器 * @company: hugeinfo * @author: liyj * @time: 2021-11-05 16:51:48 * @version: 1.0.0 */ @Slf4j @Component public class AuthFilter implements GlobalFilter, Ordered { @Value("${jwt.secret-key}") private String secretKey; @Value("${jwt.auth-skip-urls}") private String[] skipAuthUrls; @Value("${jwt.blacklist-key.format}") private String jwtBlacklistKeyFormat; /** * jwt用户 */ private static String ISSUSER = "HUGEINFO"; @Override public int getOrder() { return -100; } /** * 拦截方法 * @param exchange * @param chain * @return */ @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { //开发环境不校验 if (SpringContextUtil.checkDev()){ return chain.filter(exchange); }else { String url = exchange.getRequest().getURI().getPath(); //跳过不需要验证的路径 List skipAuthUrlList = Arrays.asList(skipAuthUrls); Boolean status = false; if (!CollectionUtils.isEmpty(skipAuthUrlList)) { for (String skipAuthUrl : skipAuthUrlList) { if (url.indexOf(skipAuthUrl) != -1) { status = true; break; } } } if (status) { return chain.filter(exchange); } else { // todo 运营中心暂时写死token if (url.indexOf("dyh-oper") != -1) { String userId = "10001"; ServerHttpRequest mutableReq = null; exchange.getRequest().mutate().header("Authorization-userId", userId).build(); ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build(); return chain.filter(mutableExchange); } else { //从请求头中取出token String token = exchange.getRequest().getHeaders().getFirst("Authorization"); //未携带token或token在黑名单内 if (StringUtils.isEmpty(token) || isBlackToken(token)) { ServerHttpResponse originalResponse = exchange.getResponse(); originalResponse.setStatusCode(HttpStatus.OK); originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); byte[] response = "{\"code\": \"401\",\"msg\": \"用户未登录,请进行登录!\"}" .getBytes(StandardCharsets.UTF_8); DataBuffer buffer = originalResponse.bufferFactory().wrap(response); return originalResponse.writeWith(Flux.just(buffer)); } else { //取出token包含的身份 Map result = verifyJWT(token); int code = (int) result.get("code"); if (code != 0) { ServerHttpResponse originalResponse = exchange.getResponse(); originalResponse.setStatusCode(HttpStatus.OK); originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String responseResult = "{\"code\": \"" + code + "\", \"msg\": \"" + result.get("msg") + "\"}"; byte[] response = responseResult.getBytes(StandardCharsets.UTF_8); DataBuffer buffer = originalResponse.bufferFactory().wrap(response); return originalResponse.writeWith(Flux.just(buffer)); } else { //将现在的request,添加当前身份 String userId = (String) result.get("userId"); ServerHttpRequest mutableReq = null; String custId = (String) result.get("custId"); if (StringUtils.isNotEmpty(custId)) { URI uri = exchange.getRequest().getURI(); URI newUri = assembleUri(uri, custId); exchange.getRequest().mutate().uri(newUri).header("Authorization-userId", userId).header("Authorization-custId", custId).build(); } else { exchange.getRequest().mutate().header("Authorization-userId", userId).build(); } ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build(); return chain.filter(mutableExchange); } } } } } } /** * JWT验证 * @param token 授权信息 * @return Map */ private Map verifyJWT(String token){ Map result = new HashMap<>(); try { Algorithm algorithm = Algorithm.HMAC256(secretKey); JWTVerifier verifier = JWT.require(algorithm) .withIssuer(ISSUSER) .build(); DecodedJWT jwt = verifier.verify(token); String userId = jwt.getClaim("userId").asString(); if (userId.isEmpty()){ result.put("code", 401); result.put("msg", "登录用户已失效,请重新登录"); }else{ result.put("code", 0); result.put("userId", userId); String custId = jwt.getClaim("custId").asString(); result.put("custId", custId); } } catch (JWTVerificationException e){ log.error("AuthFilter方法[verifyJWT]调用异常:"+e, e); if (e.getMessage().indexOf("The Token has expired") != -1){ result.put("code", 401); result.put("msg", "登录用户已失效,请重新登录!"); }else{ result.put("code", 401); result.put("msg", "登录用户已失效,请重新登录"); } } return result; } /** * 判断token是否在黑名单内 * @param token 授权信息 * @return boolean */ private boolean isBlackToken(String token){ assert token != null; // return stringRedisTemplate.hasKey(String.format(jwtBlacklistKeyFormat, token)); return false; } /** * usi追加custId * @param uri uri * @param custId custId参数 * @return * @throws UnsupportedEncodingException */ private URI assembleUri(URI uri, String custId) { StringBuilder query = new StringBuilder(); //追加custId参数 if(query.length() != 0){ query.append("&"); } query.append("custId="); query.append(custId); //转码,生成新的URI URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri(); return newUri; } } /** * -------------------_ooOoo_------------------- * ------------------o8888888o------------------ * ------------------88" . "88------------------ * ------------------(| -_- |)------------------ * ------------------O\ = /O------------------ * ---------------____/`---'\____--------------- * -------------.' \\| |// `.------------- * ------------/ \\||| : |||// \------------ * -----------/ _||||| -:- |||||- \----------- * -----------| | \\\ - /// | |----------- * -----------| \_| ''\---/'' | |----------- * -----------\ .-\__ `-` ___/-. /----------- * ---------___`. .' /--.--\ `. . __---------- * ------."" '< `.___\_<|>_/___.' >'"".------- * -----| | : `- \`.;`\ _ /`;.`/ - ` : | |----- * -----\ \ `-. \_ __\ /__ _/ .-` / /----- * ======`-.____`-.___\_____/___.-`____.-'====== * -------------------`=---=' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * ---------佛祖保佑---hugeinfo---永无BUG---------- */