zoukankan      html  css  js  c++  java
  • gateway + jwt 网关认证

    思路: 全局过滤器对所有的请求拦截(生成token有效期30分钟,放入redis设置有效期3天。3天之类可以通过刷新接口自动刷新,超过3天需要重新登录。)

    前端在调用接口之前先判断token是否过期(3o分钟),过期则先调刷新接口,换取新token,

    1- 引入相关jar

    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.0</version>
    </dependency>

    2- 编写Jwt工具类(生成token + 解析token)

    
    
    package spring.cloud.gateway.common;

    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import io.jsonwebtoken.ExpiredJwtException;
    import org.springframework.util.StringUtils;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;

    public class JwtUtil {
    public static final String SECRET = "qazwsx123444$#%#()*&& asdaswwi1235 ?;!@#kmmmpom in***xx**&";
    public static final String TOKEN_PREFIX = "Bearer";
    public static final String LOGIN_URL = "/token/userId/pwd";
    public static final String LOGOUT_URL = "/token/userId";
    public static final String HEADER_AUTH = "authorization";
    public static final String HEADER_USERID = "userid";
    //token超时时间
    public static final int TOKEN_EXPIRATION_MINUTE = 30;
    //token的redis超时时间
    public static final int TOKEN_REDIS_EXPIRATION_DAY = 7;


    public static String generateToken(String userId) {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, TOKEN_EXPIRATION_MINUTE); //得到前一天
    Date date = calendar.getTime();
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.format(date);
    //todo 优化token的生层规则
    HashMap<String, Object> map = new HashMap<>();
    map.put(HEADER_USERID, userId);
    String jwt = Jwts.builder()
    .setSubject(HEADER_USERID).setClaims(map)
    .setExpiration(date)
    .signWith(SignatureAlgorithm.HS512, SECRET)
    .compact();
    return TOKEN_PREFIX + " " + jwt;
    }

    public static Map<String, String> validateToken(String token) {
    HashMap<String, String> tokenMap = new HashMap<String, String>();
    if (StringUtils.isEmpty(token)) {
    return tokenMap;
    }
    try {
    Map<String, Object> tokenBody = Jwts.parser()
    .setSigningKey(SECRET)
    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
    .getBody();
    String userId = String.valueOf(tokenBody.get(HEADER_USERID));
    tokenMap.put(HEADER_USERID, userId);
    }catch (ExpiredJwtException e){
    e.printStackTrace();
    }
    return tokenMap;
    }

    /**
    * 移到jwtUtil中去
    *
    * @param token
    * @return
    */
    public static Map<String, String> validateTokenAndUser(String token, String userIdIn) {
    Map<String, String> tokenResultMap = new HashMap<>();
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(userIdIn)) {
    return tokenResultMap;
    }
    tokenResultMap = validateToken(token);
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(userIdIn)) {
    return tokenResultMap;
    }
    //判断传入的userid和token是否匹配
    String userIdOri = tokenResultMap.get(HEADER_USERID);
    if (!userIdIn.equals(userIdOri)) {
    return new HashMap<String,String>();
    }
    return tokenResultMap;
    }

    }



    
    

    3- 编写过滤器类

    package spring.cloud.gateway.filter;
    
    import java.net.URI;
    import java.util.Map;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.route.Route;
    import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.server.PathContainer;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    import spring.cloud.gateway.common.JwtUtil;
    import spring.cloud.gateway.exception.PermissionException;
    
    /**
     * 参数参考 https://blog.csdn.net/tianyaleixiaowu/article/details/83375246
     * response参考 https://bbs.csdn.net/topics/392412604?list=11074255
     */
    @Component
    public class AuthFilter implements GlobalFilter {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            HttpHeaders header = request.getHeaders();
            HttpMethod method = request.getMethod();
            String token = header.getFirst(JwtUtil.HEADER_AUTH);
            String userId = header.getFirst(JwtUtil.HEADER_USERID);
            PathContainer pathContainer = request.getPath().pathWithinApplication();
            String path = pathContainer.value();
    
            //2- 处理登录请求
            if (StringUtils.isBlank(token)) {
                //是登录接口则放行,否则返回异常
                if (path.contains(JwtUtil.LOGIN_URL) && HttpMethod.POST.equals(method)) {
                    throw new PermissionException("please login");
                }
                return chain.filter(exchange);
            }
    
            //3- 处理刷新token请求
            if (path.indexOf("refresh") >= 0) {
                //放行去掉刷新接口(在刷新前校验userId和token是否匹配)
                return chain.filter(exchange);
            }
    
            //4- 处理刷新token请求
            if (path.contains(JwtUtil.LOGOUT_URL) && HttpMethod.DELETE.equals(method)) {
                //放行去掉登出接口(在刷新前校验userId和token是否匹配)
                return chain.filter(exchange);
            }
    
            //5- 携带token请求其他业务接口
            Map<String, String> validateResultMap = JwtUtil.validateTokenAndUser(token, userId);
            if (validateResultMap == null || validateResultMap.isEmpty()) {
                throw new PermissionException("token 已经失效");
            }
            // TODO 将用户信息存放在请求header中传递给下游业务
            Route gatewayUrl = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            URI uri = gatewayUrl.getUri();
            //表示下游请求对应的服务名如 SPRING-CLOUD-SERVICE  SPRING-CLOUD-GATEWAY
            String serviceName = uri.getHost();
    
            ServerHttpRequest.Builder mutate = request.mutate();
            mutate.header("x-user-id", validateResultMap.get("userid"));
            mutate.header("x-user-name", validateResultMap.get("user"));
            mutate.header("x-user-serviceName", serviceName);
            ServerHttpRequest buildReuqest = mutate.build();
    
            //todo 如果响应中需要放数据,也可以放在response的header中
            //ServerHttpResponse response = exchange.getResponse();
            //response.getHeaders().add("new_token","token_value");
            return chain.filter(exchange.mutate().request(buildReuqest).build());
        }
    
    
    
    }

    4- 编写相关接口API

    package spring.cloud.gateway.controller;
    
    import org.springframework.web.bind.annotation.*;
    import spring.cloud.gateway.common.JwtUtil;
    
    import java.util.Map;
    
    @RestController
    @RequestMapping("/token")
    public class TokenController {
    
        /**
         * 登录接口
         * @param user(userID +pwd)
         * @return
         */
        @PostMapping("/userId/pwd")
        public String getToken(@RequestBody Map<String,String> user)  {
            //用户名密码需要加密处理
            String result = "";
            if (user == null || user.isEmpty()) {
                return result;
            }
            String userId = user.get("userId");
            String pwd = user.get("pwd");
            if (!doLogin(userId,pwd)) {
                return result;
            }
            String token = JwtUtil.generateToken(userId);
            // todo 将token放入redis中,设置超时时间为 2 * t
            return token;
        }
    
        private Boolean doLogin(String userId,String pwd) {
            //后续对接user表验证
            if ("admin".equals(userId) && "123".equals(pwd)) {
                return true;
            }
            if ("spring".equals(userId) && "123".equals(pwd)) {
                return true;
            }
            if ("gateway".equals(userId) && "123".equals(pwd)) {
                return true;
            }
            return false;
        }
    
        /**
         * 登出接口
         */
    
    
    
    
        /**
         * 刷新token的接口
         * 在刷新前校验userId和token是否匹配
         */
    
    
    }
  • 相关阅读:
    基于term vector深入探查数据
    oracle 行转列
    oracle 统计成绩
    最全最新个税计算公式---今天你税了吗?
    .net反混淆脱壳工具de4dot的使用
    使用ILSpy软件反编译.Net应用程序的方法及注意事项
    EmguCV使用Stitcher类来拼接图像
    从睡姿就可以看出你的性格,据说非常准,快存!
    分享几个.NET WinForm开源组件,纪念逐渐远去的WinForm。。。
    【转载】关于.NET下开源及商业图像处理(PSD)组件
  • 原文地址:https://www.cnblogs.com/domi22/p/10524005.html
Copyright © 2011-2022 走看看