Getway jwt 单点登录以及刷新token时间

本文详细介绍了如何在Spring Gateway中集成JWT,包括设置token过期时间、生成和验证token,以及实现单点登录和token刷新功能。重点展示了如何处理JWT工具类和在网关拦截器中应用这些功能,确保了API的安全访问。
摘要由CSDN通过智能技术生成

Getway jwt 单点登录以及刷新token时间

不用nosql或其他
1.集成jwt
pom依赖:

 <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.0</version>
    </dependency>

2.jwt工具类

package com.zkhx.uitls;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author kai
 * @since 2020
 */
public class JwtUtils {

    //设置token过期时间
//    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    public static final long EXPIRE = 2000 * 60;
    //加密秘钥,可以随便写
    public static final String KAI_SECRET = "xxxxxxxxxxxxx";

    //这个是生成token的方法,参数可多个
    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                //这个是头信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")

                //这里是设置过期时间
                .setSubject("kj-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))

                //设置token的主体信息,存储用户信息
                .claim("id", id)
                .claim("nickname", nickname)
                //.claim("xxx", xxx)

                //签发hash  根据KAI_SECRET秘钥  HS256方式生成
                .signWith(SignatureAlgorithm.HS256, KAI_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);//根据秘钥解析,如果异常,则返回false
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }

    /**
     * 根据token获取nickname
     * @param request
     * @return
     */
    public static String getNameByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("nickname");
    }

   
}

用户服务登录获取token

package com.zkhx.dept.controller;

import com.github.pagehelper.PageInfo;
import com.zkhx.dept.service.UserService;
import com.zkhx.entity.UnitEntity;
import com.zkhx.entity.UserEntity;
import com.zkhx.uitls.JwtUtils;
import com.zkhx.uitls.MD5;
import com.zkhx.uitls.ResultCode;
import com.zkhx.uitls.ResultMsg;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author qinKJ
 * @version 1.0
 * @date 2020.12.7 11:50
 */
@RestController
@Slf4j
@RequestMapping("/xxx/xxx")
@Api(tags = "",description = "")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/UserController/userLogin")
    public ResultMsg userLogin(@RequestParam("username") String username,@RequestParam("password") String password){
        try {
            UserEntity userEntity=new UserEntity();
            userEntity.setPassword(MD5.encrypt(password));
            userEntity.setUsername(username);
            UserEntity userEntity1 = userService.userLogin(userEntity);
            if(userEntity1!=null&&!userEntity1.equals("")){
                String jwtToken = JwtUtils.getJwtToken(String.valueOf(userEntity1.getId()), userEntity1.getUsername());
                return ResultMsg.successMsg().data(jwtToken);
            }else {
                return ResultMsg.failMsg(ResultCode.DATANULL,"登录失败,请检查用户名和密码是否正确");
            }
        }catch (Exception e){
            return ResultMsg.failMsg(ResultCode.ERROR,"发生异常");
        }
    }
}

在这里插入图片描述
getway服务
依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
      <version>${boot-version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      <version>${boot-version}</version>
    </dependency>

    <!--jwt-->
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.0</version>
    </dependency>

getway与web是有冲突的,servlet不能用,用 的ServerHttpRequest ServerHttpResponse
原jwt工具类是涉及到servlet的,复制改造jwt工具类到网关服务

package com.zkhx.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import java.util.Date;

/**
 * @author qinKJ
 * @version 1.0
 * @date 2020.12.30 10:55
 */
public class JwtUtil {
    public static final String KAI_SECRET = "xxxxx";

    //设置token过期时间
    //    public static final long EXPIRE = 1000 * 60 * 60 * 24;
    public static final long EXPIRE = 2000 * 60;

    //这个是生成token的方法,参数可多个
    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                //这个是头信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")

                //这里是设置过期时间
                .setSubject("kj-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))

                //设置token的主体信息,存储用户信息
                .claim("id", id)
                .claim("nickname", nickname)
                //.claim("xxx", xxx)

                //签发hash  根据KAI_SECRET秘钥  HS256方式生成
                .signWith(SignatureAlgorithm.HS256, KAI_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取nickname
     * @param jwtToken
     * @return
     */
    public static String getNameByJwtToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("nickname");
    }

    /**
     * 根据token获取会员id
     * @param jwtToken
     * @return
     */
    public static String getMemberIdByJwtToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(KAI_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }

    /**
     * 判断是可以刷新token,根据自己的业务来
     * @param token
     * @param lastPasswordReset
     * @return
     */
    public static Boolean canTokenBeRefreshed(String token, Date xxx) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(KAI_SECRET)
                    .parseClaimsJws(token)
                    .getBody();
            final Date iat = claims.getIssuedAt();
            final Date exp = claims.getExpiration();
            if (iat.before(xxx) || exp.before(new Date())) {
                return false;
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 刷新token
     * @param token
     * @param id
     * @param nickname
     * @return
     */
    public static String refreshToken(String token,String id, String nickname) {
        String refreshedToken;
        try {
            final Claims claims = Jwts.parser()
                    .setSigningKey(KAI_SECRET)
                    .parseClaimsJws(token)
                    .getBody();
            refreshedToken = getJwtToken(id, nickname);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }
}

网关拦截器

package com.zkhx.filter;

import com.google.gson.JsonObject;
import com.zkhx.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
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.HttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * @author qinKJ
 * @version 1.0
 * @date 2020.12.30 9:50
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        ServerHttpResponse response = exchange.getResponse();
        //校验用户必须登录
        if(antPathMatcher.match("/phm/fault/**", path)) {
            List<String> tokenList = request.getHeaders().get("token");
            String tokenJwt=request.getHeaders().getFirst("token");
            if(null == tokenList) {
//                ServerHttpResponse response = exchange.getResponse();
                return out(response);
            } else if(JwtUtil.checkToken(tokenJwt)) {
                String nameByJwtToken = JwtUtil.getNameByJwtToken(tokenJwt);
                String memberIdByJwtToken = JwtUtil.getMemberIdByJwtToken(tokenJwt);
****************************************************************测试用****************************************************************
                Claims claims1 = Jwts.parser()
                        .setSigningKey("ukc8BDbRigUDaY6pZFfWus2jZWLPHO")
                        .parseClaimsJws(tokenJwt)
                        .getBody();

                Date d1 = claims1.getIssuedAt();
                Date d2 = claims1.getExpiration();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                System.out.println("username参数值:" + claims1.get("username"));
                System.out.println("登录用户的id:" + claims1.getId());
                System.out.println("登录用户的名称:" + claims1.getSubject());
                System.out.println("令牌签发时间:" + sdf.format(d1));
                System.out.println("令牌过期时间:" + sdf.format(d2));

                Boolean aBoolean = JwtUtil.canTokenBeRefreshed(tokenJwt, new Date());
                if (!aBoolean){
                    tokenJwt=JwtUtil.refreshToken(tokenJwt,memberIdByJwtToken,nameByJwtToken);
                    Claims claims2 = Jwts.parser()
                            .setSigningKey(JwtUtil.KAI_SECRET)
                            .parseClaimsJws(tokenJwt)
                            .getBody();

                    Date d3 = claims2.getIssuedAt();
                    Date d4 = claims2.getExpiration();
                    System.out.println("username参数值:" + claims2.get("username"));
                    System.out.println("登录用户的id:" + claims2.getId());
                    System.out.println("登录用户的名称:" + claims2.getSubject());
                    System.out.println("令牌签发时间:" + sdf.format(d3));
                    System.out.println("令牌过期时间:" + sdf.format(d4));

                    // 获取响应Header,目前的实现中返回的是HttpHeaders实例,可以直接修改
                    response.getHeaders().add("new_token",tokenJwt);
                }
****************************************************************测试用****************************************************************
                // TODO 将token信息存放在请求header中传递给下游业务
                ServerHttpRequest.Builder mutate = request.mutate();
                mutate.header("AuthToken", tokenJwt);
                ServerHttpRequest buildReuqest = mutate.build();
                return chain.filter(exchange.mutate()
                        .request(buildReuqest)
                        .response(response)
                        .build());
            }else {
//                ServerHttpResponse response = exchange.getResponse();
                return out(response);
            }
        }
        //内部服务接口,不允许外部访问
        if(antPathMatcher.match("/**/knowledge/**", path)) {
//            ServerHttpResponse response = exchange.getResponse();
            return out(response);
        }
        return chain.filter(exchange);
    }

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

    private Mono<Void> out(ServerHttpResponse response) {
        JsonObject message = new JsonObject();
        message.addProperty("success", false);
        message.addProperty("code", 28004);
        message.addProperty("data", "鉴权失败");
        byte[] bits = message.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        //response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }
}

接下来是给访问服务加过滤
在这里插入图片描述

package com.zkhx.config;

/**
 * @author qinKJ
 * @version 1.0
 * @date 2020.12.30 14:13
 */

import com.zkhx.filter.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * 拦截器配置
 */
@Configuration
public class InterCepTerConfig implements WebMvcConfigurer {

    private TokenInterceptor tokenInterceptor;

    //构造方法
    public InterCepTerConfig(TokenInterceptor tokenInterceptor){
        this.tokenInterceptor = tokenInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        List<String> excludePath = new ArrayList<>();
//        excludePath.add("/user_register"); //注册
//        excludePath.add("/login"); //登录
//        excludePath.add("/logout"); //登出
//        excludePath.add("/static/**");  //静态资源
//        excludePath.add("/swagger-ui.html/**");  //静态资源
//        excludePath.add("/assets/**");  //静态资源

        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);

    }

    /**
     * 跨域支持
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
                .maxAge(3600 * 24);
    }


}

package com.zkhx.filter;

import com.alibaba.fastjson.JSONObject;
import com.zkhx.uitls.JwtUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author qinKJ
 * @version 1.0
 * @date 2020.12.30 14:07
 * 防止直接访问
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception{

        if(request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        response.setCharacterEncoding("utf-8");

        String token = request.getHeader("AuthToken");
        if(token != null){
            boolean result = JwtUtils.checkToken(token);
            if(result){
                return true;
            }
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null;
        try{
            JSONObject json = new JSONObject();
            json.put("success","false");
            json.put("msg","不能从这里访问哟");
            json.put("code","50000");
            response.getWriter().append(json.toJSONString());
            System.out.println("认证失败,未通过拦截器");
            //        response.getWriter().write("50000");
        }catch (Exception e){
            e.printStackTrace();
            response.sendError(500);
            return false;
        }

        return false;

    }
}

完毕,不通过网关访问接口:
在这里插入图片描述
登录服务获取到token,通过postman调试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
替换token后再次访问
在这里插入图片描述
刷新成功

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值