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元。

浙公网安备 33010602011771号