jwt生成/验证token,feign的token传递,RestTemplate传递token

Posted 好大的月亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jwt生成/验证token,feign的token传递,RestTemplate传递token相关的知识,希望对你有一定的参考价值。

jwt的组成

jwt全称=Json Web token

在这里插入图片描述

jwt公式demo
在这里插入图片描述

接着上依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.7</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.7</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.7</version>
    <scope>runtime</scope>
</dependency>

jwt加密与解密工具类

package com.fchen.usercenter.util;

import com.google.common.collect.Maps;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@SuppressWarnings("WeakerAccess")
@Component
public class JwtOperator {
    /**
     * 秘钥
     * - 默认aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt
     */
    @Value("${secret:aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt}")
    private String secret;
    /**
     * 有效期,单位秒
     * - 默认2周
     */
    @Value("${expire-time-in-second:1209600}")
    private Long expirationTimeInSecond;

    /**
     * 从token中获取claim
     *
     * @param token token
     * @return claim
     */
    public Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(this.secret.getBytes())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
            log.error("token解析错误", e);
            throw new IllegalArgumentException("Token invalided.");
        }
    }

    /**
     * 获取token的过期时间
     *
     * @param token token
     * @return 过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimsFromToken(token)
                .getExpiration();
    }

    /**
     * 判断token是否过期
     *
     * @param token token
     * @return 已过期返回true,未过期返回false
     */
    private Boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 计算token的过期时间
     *
     * @return 过期时间
     */
    private Date getExpirationTime() {
        return new Date(System.currentTimeMillis() + this.expirationTimeInSecond * 1000);
    }

    /**
     * 为指定用户生成token
     *
     * @param claims 用户信息
     * @return token
     */
    public String generateToken(Map<String, Object> claims) {
        Date createdTime = new Date();
        Date expirationTime = this.getExpirationTime();


        byte[] keyBytes = secret.getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(createdTime)
                .setExpiration(expirationTime)
                // 你也可以改用你喜欢的算法
                // 支持的算法详见:https://github.com/jwtk/jjwt#features
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 判断token是否非法
     *
     * @param token token
     * @return 未过期返回true,否则返回false
     */
    public Boolean validateToken(String token) {
        return !isTokenExpired(token);
    }

    public static void main(String[] args) {
        // 1. 初始化
        JwtOperator jwtOperator = new JwtOperator();
        jwtOperator.expirationTimeInSecond = 1209600L;
        jwtOperator.secret = "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrsssttt";

        // 2.设置用户信息
        HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
        objectObjectHashMap.put("id", "1");

        // 测试1: 生成token
        String token = jwtOperator.generateToken(objectObjectHashMap);
        // 会生成类似该字符串的内容: eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk4MTcsImV4cCI6MTU2Njc5OTQxN30.27_QgdtTg4SUgxidW6ALHFsZPgMtjCQ4ZYTRmZroKCQ
        System.out.println(token);

        // 将我改成上面生成的token!!!
        String someToken = "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE2MjE5NTU2NzUsImV4cCI6MTYyMzE2NTI3NX0.yrfiivBLq_7uFh1l5f3RpeUdDrH4L9ykso2eUp86Li0";
        // 测试2: 如果能token合法且未过期,返回true
        Boolean validateToken = jwtOperator.validateToken(someToken);
        System.out.println(validateToken);

        // 测试3: 获取用户信息
        Claims claims = jwtOperator.getClaimsFromToken(someToken);
        System.out.println(claims);

        // 将我改成你生成的token的第一段(以.为边界)
        String encodedHeader = "eyJhbGciOiJIUzI1NiJ9";
        // 测试4: 解密Header
        byte[] header = Base64.decodeBase64(encodedHeader.getBytes());
        //{"alg":"HS256"}
        System.out.println(new String(header));

        // 将我改成你生成的token的第二段(以.为边界)
        String encodedPayload = "eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk1NDEsImV4cCI6MTU2Njc5OTE0MX0";
        // 测试5: 解密Payload
        byte[] payload = Base64.decodeBase64(encodedPayload.getBytes());
        //{"id":"1","iat":1565589541(token的生成时间),"exp":1566799141(token的过期时间)}
        System.out.println(new String(payload));

        // 测试6: 这是一个被篡改的token,因此会报异常,说明JWT是安全的
        jwtOperator.validateToken("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk3MzIsImV4cCI6MTU2Njc5OTMzMn0.nDv25ex7XuTlmXgNzGX46LqMZItVFyNHQpmL9UQf-aUx");
    }
}

feign的token传递

Feign里的@RequestHeader是把token放到http请求头里传给接收方
在这里插入图片描述

feign使用RequestInterceptor传递token,配置全局生效,所有的feign接口在调用的时候都在header上添加token信息

编写一个class实现RequestInterceptor来传递toekn

package com.fchen.usercenter.intercept;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class TokenRelayRequestIntercept implements RequestInterceptor {


    @Override
    public void apply(RequestTemplate template) {
        //获取token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
        //根据 header 里的约定key值获取token
        String token = httpServletRequest.getHeader("x-token");

        //传递token
        if(!StringUtils.isEmpty(token)){
            template.header("x-token",token);
        }
    }
}

然后通过yaml来配置生效,也可以通过代码来配置生效,我这边使用了yaml的demo

feign:
  # feign通过yml参数将调用所有微服务的时候的日志等级改为 BASIC (当然前提还是需要Feign本身接口的日志等级=debug,45行的配置)
  client:
    config:
      #将调用的微服务名称改成default就配置成全局的了
      default:
        loggerLevel: BASIC
        requestInterceptors:
          - com.fchen.usercenter.intercept.TokenRelayRequestIntercept

在这里插入图片描述

RestTemplate传递token

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;


@Autowired
private RestTemplate restTemplate;

@GetMapping("testRestTemplateToekn")
public ResponseEntity<String> testRestTemplateToekn(HttpServletRequest request) throws JsonProcessingException {
    Map<String,Object> map = new HashMap(){{
        put("time", LocalDateTime.now());
    }};

    HttpHeaders headers = new HttpHeaders();
    headers.add("x-token", request.getHeader("x-token"));
    headers.add("x-token", "I'm toekn");
    headers.add("x-token", "I'm toekn");

    return restTemplate.exchange(
            "http://www.baidu.com",
            HttpMethod.POST,
            new HttpEntity<>(headers),
            String.class,
            map
    );
}

RestTemplate使用ClientHttpRequestInterceptor全局添加token

先配置class实现ClientHttpRequestInterceptor接口后编写添加token,然后配置生效

package com.fchen.usercenter.intercept;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class RestTemplateRelayInterceptor implements ClientHttpRequestInterceptor {


    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        //获取token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
        //根据 header 里的约定key值获取token
        String token = httpServletRequest.getHeader("x-token");

        HttpHeaders headers = request.getHeaders();
        headers.add("x-token",token);
        return execution.execute(request, body);
    }
}

配置bean生效resttemplate
在这里插入图片描述

@Bean
//@LoadBalanced
public RestTemplate restTemplate(HttpClientPoolConfig httpClientPoolConfig){
    RestTemplate restTemplate = new RestTemplate(httpClientPoolConfig.httpRequestFactory());
    restTemplate.setInterceptors(
            Collections.singletonList(new RestTemplateRelayInterceptor())
    );
    return restTemplate;
}

以上是关于jwt生成/验证token,feign的token传递,RestTemplate传递token的主要内容,如果未能解决你的问题,请参考以下文章

登陆权限--token 的生成和验证

token 创建与验证 (tp6)

JWT

基于Token的身份验证——JWT

JWT生成token及过期处理方案

token令牌和jwt