一、登录token验证方式:

session方式:

  用户进行用户登录,服务端会验证用户名和密码,成功后会生成一个session,它存储在服务端,并且要有与之对应的Cookie中的sessionId,

不适合集群搭建。

JWT方式: 

    day02-09-网关鉴权流程分析(视频)

  1. 自媒体和管理端使用用户名和密码登录,服务端收到请求,去验证用户名与密码是否一致
  2. 如果一致,通过jwt生成token(令牌),token里面包含有用户的信息
  3. 登陆完成后服务端把token再返回给前端,前端拿到后缓存到浏览器里面,以后前端访问其他请求时都会携带token
  4. 在网关中统一校验token是否存在且合法,如果合法解析出userId放到请求头中传递到微服务,
  5. 在微服务中 我们采用拦截器获取userId放到ThreadLocal中,这样在Controller和service层都可以获取userId

注意细节:

  1、密码采用的是md5加密算法+随机盐的方式,随机盐是在新增用户时随机产生的  

  2、jwt 包含三部分内容,头、载荷、签名,其中头和载荷用的是base64位加密算法,签名用的是你选择的比如HS256 RS256

  3、不用ThreadLocal可以吗,可以,但是这样就需要在每个Controller的方法中使用request.getHeader方法获取userId了

  4、token生成的时候是否有过期时间?有,两个小时 ?

  5、token怎么续签(续期),前端每隔一段时间发起重新获取token的请求

  6、退出登录,token怎么处理?放到了Redis中,标记为黑名单,就是标记成这个token不能用了

 

java jwt token 验证 jwt到底怎么验证token合法性_微服务

 

 

二、拦截器应用

JWT工具类:

import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class AppJwtUtil {

    // TOKEN的有效期一天(MS)
    private static final int TOKEN_TIME_OUT = 3600 * 1000 * 24;
    // 加密KEY
    private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
    // 最小刷新间隔(S)
    private static final int REFRESH_TIME = 300;

    /**
     * 生成token
     *
     * @param claimMaps 自定义信息
     * @return
     */
    public static String getToken(Map<String, Object> claimMaps) {
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(currentTime))  //签发时间
                .setSubject("system")  //说明
                .setIssuer("heima") //签发者信息
                .setAudience("app")  //接收用户
                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT))  //过期时间戳
                .addClaims(claimMaps) //cla信息
                .compact();
    }

    /**
     * 获取token中的claims信息
     *
     * @param token
     * @return
     */
    private static Jws<Claims> getJws(String token) {
        return Jwts.parser()
                .setSigningKey(generalKey())
                .parseClaimsJws(token);
    }

    /**
     * 获取payload body信息
     *
     * @param token
     * @return
     */
    public static Claims getClaimsBody(String token) {
        try {
            return getJws(token).getBody();
        } catch (ExpiredJwtException e) {
            return null;
        }
    }

    /**
     * 获取hearder body信息
     *
     * @param token
     * @return
     */
    public static JwsHeader getHeaderBody(String token) {
        return getJws(token).getHeader();
    }

    /**
     * 是否过期
     *
     * @param claims
     * @return -1:有效,0:有效,1:过期,2:过期
     */
    public static int verifyToken(Claims claims) {
        if (claims == null) {
            return 1;
        }
        try {
            claims.getExpiration()
                    .before(new Date());
            if ((claims.getExpiration().getTime() - System.currentTimeMillis()) > REFRESH_TIME * 1000) {
                return -1;
            } else {
                return 0;
            }
        } catch (ExpiredJwtException ex) {
            return 1;
        } catch (Exception e) {
            return 2;
        }
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("userId", "10001");
        map.put("userName", "tom");
        String token = AppJwtUtil.getToken(map);
        System.out.println(token);
        Jws<Claims> jws = AppJwtUtil.getJws(token);
        Claims claims = jws.getBody();
        System.out.println(claims.get("userId"));
        System.out.println(claims.get("userName"));
    }

}

拦截器配置:

package com.gateway.filter;

import com.gateway.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
//        判断是否为登录操作  如果是登录操作直接放行
        String path = request.getURI().getPath();  // api/v1/channel/list  /login/in
        if(path.contains("/login")){
            return chain.filter(exchange);  //直接放行
        }

        String token = request.getHeaders().getFirst("token");
 //        判断请求头中是否携带token 如果没有直接返回401
        if(StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

//        如果携带token判断token是否有效
        Claims claims = AppJwtUtil.getClaimsBody(token);
        if(claims!=null){
            //        如果有效从token中解析出userId和name放到请求头中继续进入到后面的微服务中
            int type = AppJwtUtil.verifyToken(claims);

//            -1:有效,0:有效
            if(type==-1||type==0){
                Integer userId = claims.get("userId", Integer.class);
                String name = claims.get("name", String.class);
                request.mutate().header("userId",userId.toString());
                request.mutate().header("name",name);
                return chain.filter(exchange);  //放行
            }
        }

        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

测试效果:

启动admin服务,继续访问其他微服务,会提示需要认证才能访问,这个时候需要在heads中设置设置token才能正常访问。