java使用JWT技术

java的前端请求token往往需要自己写一个, 目前大多数技术使用session就是JWT技术实现。废话不多说,直接代码

 

1.先来个依赖包:

<!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

2.心情好,直接来JWT的工具类:

JwtTokenUtil

package com.hbg.hbgserver.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtTokenUtil {
    private static final String SECRET = "hbg&^%server};()@_2020";

    public static class JwtMessage{
        public DecodedJWT jwt;
        public boolean result;
        public String errorMessage;

        public DecodedJWT getJwt() {
            return jwt;
        }

        public void setJwt(DecodedJWT jwt) {
            this.jwt = jwt;
        }

        public boolean isResult() {
            return result;
        }

        public void setResult(boolean result) {
            this.result = result;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public void setErrorMessage(String errorMessage) {
            this.errorMessage = errorMessage;
        }
    }

    //Header
    private static Map<String, Object> generateHeader(String jwt){
        Map<String, Object> header = new HashMap<>();
        header.put("typ","JWT");
        switch(jwt){
            case "HS256":
                header.put("alg","HMAC256");
                break;
            case "HS384":
                header.put("alg","HMAC256");
                break;
            case "HS512":break;
            case "RS256":break;
            case "RS384":break;
            case "RS512":break;
            case "ES256":break;
            case "ES384":break;
            case "ES512":break;
            default:
                header.put("alg","HMAC256");
                break;
        }
        return header;
    }

    public static DecodedJWT decodedJWT(String token){
        try {
            DecodedJWT jwt = JWT.decode(token);
            return  jwt;
        } catch (JWTDecodeException exception){
            //Invalid token
        }
        return null;
    }

    public static String createToken(String from, String userId) {
        //过期时间(秒)
        long expiredMinutes = 1000 * 60 * 60 * 24 * 7;
        return createToken(from, userId, expiredMinutes);
    }

    public static String createToken(String from, String userId,long expireTime) {
        //过期时间(秒)
        long now = System.currentTimeMillis();
        try {
            String token = JWT.create()
                    //header
                    .withHeader(generateHeader("HS256"))
                    //payload
                    .withIssuer("hbg")//JWT的签发主体
                    .withIssuedAt(new Date(now))//JWT的签发时间
                    .withSubject("hbg-server")//JWT的主体,即它的所有人
                    .withAudience(from)//JWT的接收对象
                    .withExpiresAt(new Date(now + expireTime)) //JWT的过期时间 30分钟
                    .withNotBefore(new Date(now))//JWT生效的开始时间
                    .withClaim("userId", userId)
                    //signature
                    .sign(Algorithm.HMAC256(SECRET));
            System.out.println("token:"+token);
            return token;
        }  catch (JWTCreationException exception){
            //Invalid Signing configuration / Couldn't convert Claims.
        }

        return null;

    }

    /**
     * 验证
     * @param token
     *
     * iss(Issuser):代表这个JWT的签发主体;
       sub(Subject):代表这个JWT的主体,即它的所有人;
       aud(Audience):代表这个JWT的接收对象;
       exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
       nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
       iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
       jti(JWT ID):是JWT的唯一标识。
     *
     * @return
     */
    public static JwtMessage verify(String token){
        JwtMessage jwtMessage = new JwtMessage();
        jwtMessage.setResult(false);
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("hbg")
                    .build(); //Reusable verifier instance
            JWT.decode(token);
            DecodedJWT jwt = verifier.verify(token);
            jwtMessage.setJwt(jwt);
            jwtMessage.setResult(true);
            return jwtMessage;

        } catch (JWTVerificationException exception){
            //Invalid signature/claims
            System.out.println(exception.getMessage());
            if(exception.getMessage().indexOf("expired")>0){
                jwtMessage.setErrorMessage("token-expired");
            }

        }

        return jwtMessage;
    }

    //每次请求验证token
    public static boolean checkToken(HttpServletRequest request, HttpServletResponse response){
        String token = request.getHeader("token");
        //log.info("headerToken:"+token);
        if ((token != null) && (token.length() > 7)) {
                JwtMessage jwtMessage = verify(token);
                if(jwtMessage.isResult() ){
                    DecodedJWT jwt = jwtMessage.getJwt();
                    // System.out.println("jwt:"+(jwt == null));
                    if (jwt != null) {
                        //当旧Token快要过期时 ,发放新的token
                        String newToken = updateToken(jwt);
                        response.setHeader("token",newToken);
                        return true;
                    }
                }
        }
        return false;
    }

    public static String updateToken(DecodedJWT jwt) {
        //StaticLog.info("from {},accountId:{}",jwt.getClaim("from").asString(),jwt.getClaim("accountId").asString());
        Date expireTime = jwt.getExpiresAt();
        long expireMillis = DateUtil.millsecond(expireTime);
        Date signTime = jwt.getIssuedAt();
        long signMillis = DateUtil.millsecond(signTime);
        long now = System.currentTimeMillis();
        double a = (expireMillis - signMillis) *1.0 / (expireMillis - now);
        if(a>=0.1){
            System.out.println("get new jwt ");
            return createToken(jwt.getClaim("from").asString(),jwt.getClaim("user_id").asString());
        }
        return jwt.getToken();
    }

    /**
     * 根据token获取userId
     * @param token
     * @return
     */
    public static Long getUserIdByToken(String token){
        DecodedJWT jwt = JWT.decode(token);
        Map<String, Claim> claims = jwt.getClaims();
        if(claims.get("userId").isNull() || StrUtil.isBlank(claims.get("userId").asString()
        )){
            return null;
        }
        Long userId = null;
        try {
            userId = Long.parseLong(claims.get("userId").asString());
        }catch (Exception e){
            e.printStackTrace();
        }
        return userId;
    }

    public static void main(String[] args) {
        String token1 = createToken("login","1", 1000*60*60 *24*7);

        System.out.println(token1);
        System.out.println(verify(token1));

    }

}

 

上面基本可以说完成了JTW的基本配置了

怎么使用了?  真实业务实现

 

1.先来个查询

    @Authorize(required = false)
    @ApiOperation(value = "获取Token")
    @GetMapping(value = "/getToken")
    public JsonResult<String> getToken(String tokenId) {
        String token1 = JwtTokenUtil.createToken("login",tokenId, 1000*60*60 *24*7);
       // String token1 = JwtTokenUtil.createToken("login","69", 1000*60*60 *24*7);
        // testMultiLock();
        //testLock();
        return JsonResult.success(token1);
    }

2。来个Controller成继承的基本token

package com.hbg.hbgserver.model.dto.request;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.hbg.hbgserver.util.JwtTokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

public class BaseUserInfoRequest {

    @JsonIgnore
    private String token;

    public BaseUserInfoRequest(){
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(sra != null){
            HttpServletRequest request = sra.getRequest();
            token = request.getHeader("token");
        }
    }

    @JsonIgnore
    public Long getUserId(){
        if(StringUtils.isNotBlank(token)){
            return JwtTokenUtil.getUserIdByToken(token);
        }
        return null;
    }


}

 

 

3.来个注册流程:

 public LoginResponse loginByPhone(LoginRequest loginRequest) {
        // 检验验证码
        boolean isSmsCodeValid = smsCodeManager.checkValidateCode(loginRequest.getPhone(), loginRequest.getVerifyCode());
        Assert.isTrue(isSmsCodeValid, BusinessErrorEnum.SMS_CODE_INVALID);

        UserInfo userInfo = userInfoManager.getByPhoneNumber(loginRequest.getPhone());
        if (userInfo == null) {
            // 走注册流程
            userInfo = ((UserInfoService) AopContext.currentProxy()).register(RegisterBO.builder().loginPlatform(loginRequest.getLoginPlatform())
                    .registerType(UserInfoConstants.LoginType.LOGIN_BY_PHONE)
                    .phoneNumber(loginRequest.getPhone()).build());
        }
        userInfoManager.lambdaUpdate().set(UserInfo::getLastLoginTime, LocalDateTime.now()).eq(UserInfo::getId, userInfo.getId()).update();
        String token = JwtTokenUtil.createToken("login", userInfo.getId().toString());
        return LoginResponse.builder().token(token).hasBindPhone(StrUtil.isNotEmpty(userInfo.getPhoneNumber())).build();
    }

 

重点:那就是说明,没有解释的代码都是耍流氓。  通过工具类

JwtTokenUtil.createToken 生成我们需要的token,

重点就是如何使用。

看上面的代码,
 String token = JwtTokenUtil.createToken("login", userInfo.getId().toString());你用生成的用户id生成我们需要token,
然后返回给前端。让前端保存这个token值请求头部信息带回来,校验。



更重点来了,系统如何拦截校验这个token。

1.先写一个注解类
/**
 * 验证token注解
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Authorize {
    boolean required() default true;
}

 

2.可以通过shiro 或者aop机制完成校验。下面我们就通过aop模式完成token的校验

JwtValidateAspect:

 

package com.hbg.hbgserver.aspect;

import com.hbg.common.enums.BusinessErrorEnum;
import com.hbg.common.model.vo.JsonResult;
import com.hbg.hbgserver.annotations.Authorize;
import com.hbg.hbgserver.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 通过aop验证token
 */
@Aspect
@Component
@Slf4j
@Order(2)
public class JwtValidateAspect {
    @Pointcut("execution(public * com.hbg.hbgserver.controller..*.*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //System.out.println("income aop");
        Object obj = null;
        try {
            //RequestContextHolder:持有上下文的Request容器,获取到当前请求的request
            ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (null == sra) {
                log.error("ServletRequestAttributes is null ");
                return joinPoint.proceed();
            }
            HttpServletRequest request = sra.getRequest();
            HttpServletResponse response = sra.getResponse();

            Object target = joinPoint.getTarget();
            //从类或方法上拿鉴权注解
            Authorize annotation = target.getClass().getAnnotation(Authorize.class);
            if (annotation == null) {
                //获取注解属性
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                // 默认都需要校验,除非添加了 @Authorize(required=false)
                annotation = signature.getMethod().getAnnotation(Authorize.class);
            }
            if (annotation == null || annotation.required()) {
                if (!JwtTokenUtil.checkToken(request, response)) {
                    return JsonResult.fail(BusinessErrorEnum.USER_NOT_LOGIN);
                }
            }
            //启动目标方法
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            throw e;
        }
        return obj;
    }
}

 

上面就是通过aop机制完成token注解的一个拦截   

 

实现  不需要token直接访问就加上注解:@Authorize(required = false)

 

整个JTW从生产到,使用。再到校验拦截。完成整个系统。

实在看不懂,可以联系我购买源码-保证实用- 一次服务10元。

 

posted @ 2020-11-10 14:29  技术专家  阅读(662)  评论(0)    收藏  举报