包含javajwtutil的词条

博主:adminadmin 2022-12-11 00:15:08 62

本篇文章给大家谈谈javajwtutil,以及对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

JWT的TOKEN续期功能

JWT里有一个关键的东东,就是续期TOKEN,即TOKEN快过期时,刷新一个新的TOKEN给客户端.

办法如下:

1.后端生成TOKEN

importcom.starmark.core.shiro.model.SecurityUser;importcom.starmark.core.shiro.model.UserLoginToken;importcom.starmark.core.shiro.util.JWTUtil;importorg.apache.commons.lang3.BooleanUtils;importorg.apache.commons.lang3.StringUtils;importorg.apache.shiro.authc.AuthenticationException;importorg.apache.shiro.authc.AuthenticationToken;importorg.apache.shiro.subject.Subject;importorg.apache.shiro.web.filter.authc.AuthenticatingFilter;importorg.apache.shiro.web.util.WebUtils;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.web.bind.annotation.RequestMethod;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.time.LocalDateTime;importjava.time.ZoneId;importjava.util.Date;importjava.util.Objects;publicclassJwtAuthFilterextendsAuthenticatingFilter{privatefinalLoggerlog=LoggerFactory.getLogger(JwtAuthFilter.class);//10分钟后刷新tokenprivatestaticfinalinttokenRefreshInterval=60*10;@OverrideprotectedbooleanpreHandle(ServletRequestrequest,ServletResponseresponse)throwsException{HttpServletRequesthttpServletRequest=WebUtils.toHttp(request);if(httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name()))//对于OPTION请求做拦截,不做token校验returnfalse;returnsuper.preHandle(request,response);}@OverrideprotectedvoidpostHandle(ServletRequestrequest,ServletResponseresponse){request.setAttribute("jwtShiroFilter.FILTERED",true);}@OverrideprotectedbooleanisAccessAllowed(ServletRequestrequest,ServletResponseresponse,ObjectmappedValue){if(this.isLoginRequest(request,response)){returntrue;}BooleanafterFiltered=(Boolean)(request.getAttribute("jwtShiroFilter.FILTERED"));if(BooleanUtils.isTrue(afterFiltered))returntrue;booleanallowed=false;try{allowed=executeLogin(request,response);}catch(IllegalStateExceptione){//not found any tokenlog.error("Not found any token");}catch(Exceptione){log.error("Error occurs when login",e);}returnallowed||super.isPermissive(mappedValue);}@OverrideprotectedAuthenticationTokencreateToken(ServletRequestservletRequest,ServletResponseservletResponse){StringjwtToken=getAuthzHeader(servletRequest);if(StringUtils.isNotBlank(jwtToken)!JWTUtil.isTokenExpired(jwtToken))returnUserLoginToken.buildPassword(jwtToken,null,"jwt");returnnull;}@OverrideprotectedbooleanonAccessDenied(ServletRequestservletRequest,ServletResponseservletResponse)throwsException{HttpServletResponsehttpResponse=WebUtils.toHttp(servletResponse);httpResponse.sendRedirect("/unauth");returnfalse;}@OverrideprotectedbooleanonLoginSuccess(AuthenticationTokentoken,Subjectsubject,ServletRequestrequest,ServletResponseresponse){HttpServletResponsehttpResponse=WebUtils.toHttp(response);if(tokeninstanceofUserLoginToken"jwt".equalsIgnoreCase(((UserLoginToken)token).getLoginType())){UserLoginTokenjwtToken=(UserLoginToken)token;booleanshouldRefresh=shouldTokenRefresh(Objects.requireNonNull(JWTUtil.getIssuedAt(jwtToken.getUsername())));if(shouldRefresh){//生成新的TOKENSecurityUseruser=(SecurityUser)subject.getPrincipal();StringnewToken=JWTUtil.sign(user.getUserInfo().getId());httpResponse.setHeader("x-auth-token",newToken);}}returntrue;}@OverrideprotectedbooleanonLoginFailure(AuthenticationTokentoken,AuthenticationExceptione,ServletRequestrequest,ServletResponseresponse){log.error("Validate token fail, token:{}, error:{}",token.toString(),e.getMessage());returnfalse;}/**

    * 获取TOKEN

    * @param request 请求

    * @return token

    */privateStringgetAuthzHeader(ServletRequestrequest){HttpServletRequesthttpRequest=WebUtils.toHttp(request);Stringheader=httpRequest.getHeader("x-auth-token");returnStringUtils.removeStart(header,"Bearer ");}/**

    * 判断是否需要刷新TOKEN

    * @param issueAt token签发日期

    * @return 是否需要刷新TOKEN

    */privatebooleanshouldTokenRefresh(DateissueAt){LocalDateTimeissueTime=LocalDateTime.ofInstant(issueAt.toInstant(),ZoneId.systemDefault());returnLocalDateTime.now().minusSeconds(tokenRefreshInterval).isAfter(issueTime);}}

原签发TOKEN后10分钟后刷新新的TOKEN

2.前端获取TOKEN

// 拦截响应response,并做一些错误处理axios.interceptors.response.use((response)={if(response.status===200response.dataresponse.data.code===401){//console.log(window.location.origin);window.location.href=window.location.origin+window.location.pathname+'#/login';}//获取返回的TOKENconsttoken=response.headers['x-auth-token'];if(token){//将续期的TOKEN存起来localStorage.setItem("token",token);}// 这里是填写处理信息returnresponse;},(err)={// 这里是返回状态码不为200时候的错误处理console.log(err);if(errerr.response){switch(err.response.data.code){case400:err.message='请求错误';break;case401:err.message='未授权,请登录';break;case403:err.message='无权限';break;case404:err.message=`请求地址出错: ${err.response.config.url}`;break;case408:err.message='请求超时';break;case500:err.message='服务器内部错误';break;case501:err.message='服务未实现';break;case502:err.message='网关错误';break;case503:err.message='服务不可用';break;case504:err.message='网关超时';break;case505:err.message='HTTP版本不受支持';break;default:}}Vue.prototype.$message.error(err.response.data.msg!=null?err.response.data.msg:err.message);returnPromise.reject(err)});

注意一点,需要通过过滤器调整FITLER,增加Access-Control-Expose-Headers的输出,否则无法获取response中的header.

至此,JWT的TOKEN续期功能完成.

如何在Java 中创建和验证JWT

用户发起登录请求,服务端创建一个加密后的jwt信息,作为token返回值,在后续请求中jwt信息作为请求头,服务端正确解密后可获取到存储的用户信息,表示验证通过;解密失败说明token无效或者已过期。

加密后jwt信息如下所示,是由.分割的三部分组成,分别为Header、Payload、Signature。

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJqd3QiLCJpYXQiOjE0NzEyNzYyNTEsInN1YiI6IntcInVzZXJJZFwiOjEsXCJyb2xlSWRcIjoxfSIsImV4cCI6MTQ3MTMxOTQ1MX0.vW-pPSl5bU4dmORMa7UzPjBR0F6sqg3n3hQuKY8j35o

Header包含两部分信息,alg指加密类型,可选值为HS256、RSA等等,typ=JWT为固定值,表示token的类型。

{

"alg": "HS256",

"typ": "JWT"

}

Payload是指签名信息以及内容,一般包括iss (发行者), exp (过期时间), sub(用户信息), aud (接收者),以及其他信息,详细介绍请参考官网。

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

Signature则为对Header、Payload的签名。

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

在jwt官网,可以看到有不同语言的实现版本,这里使用的是Java版的jjwt。话不多说,直接看代码,加解密都很简单:

/**

* 创建 jwt

* @param id

* @param subject

* @param ttlMillis

* @return

* @throws Exception

*/

public String createJWT(String id, String subject, long ttlMillis) throws Exception {

SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256 ;

long nowMillis = System. currentTimeMillis();

Date now = new Date( nowMillis);

SecretKey key = generalKey();

JwtBuilder builder = Jwts. builder()

.setId(id)

.setIssuedAt(now)

.setSubject(subject)

.signWith(signatureAlgorithm, key);

if (ttlMillis = 0){

long expMillis = nowMillis + ttlMillis;

Date exp = new Date( expMillis);

builder.setExpiration( exp);

}

return builder.compact();

}

/**

* 解密 jwt

* @param jwt

* @return

* @throws Exception

*/

public Claims parseJWT(String jwt) throws Exception{

SecretKey key = generalKey();

Claims claims = Jwts. parser()

.setSigningKey( key)

.parseClaimsJws( jwt).getBody();

return claims;

}

加解密的key是通过固定字符串转换而生成的;subject为用户信息的json字符串;ttlMillis是指token的有效期,时间较短,需要定时更新。

这里要介绍的token刷新方式,是在生成token的同时生成一个有效期较长的refreshToken,后续由客户端定时根据refreshToken来获取最新的token。浏览器与服务端之间建立sse(server send event)请求,来实现刷新。关于sse在前面博文中有介绍过,此处略过不提。

JWT token封装以及自动刷新方案建议

什么是JWT

pom.xml

JWTUtil.java

用户登录操作

在前后分离场景下,越来越多的项目使用jwt token作为接口的安全机制,但存在jwt过期后,用户无法直接感知,假如在用户操作页面期间,突然提示登录,则体验很不友好,所以就有了token自动刷新需求;

方案:前端控制检测token,无感知刷新

用户登录成功的时候,一次性给他两个Token,分别为AccessToken和RefreshToken

AccessToken有效期较短,比如1天或者5天,用于正常请求

RefreshToken有效期可以设置长一些,例如10天、20天,作为刷新AccessToken的凭证

刷新方案:当AccessToken即将过期的时候,例如提前30分钟,客户端利用RefreshToken请求指定的API获取新的AccessToken并更新本地存储中的AccessToken

核心逻辑

1、登录成功后,jwt生成AccessToken; UUID生成RefreshToken并存储在服务端redis中,设置过期时间

2、接口返回3个字段AccessToken/RefreshToken/访问令牌过期时间戳

3、由于RefreshToken存储在服务端redis中,假如这个RefreshToken也过期,则提示重新登录;

老王的疑问:RefreshToken有效期那么长,和直接将AccessToken的有效期延长有什么区别

答:RefreshToken不像AccessToken那样在大多数请求中都被使用,主要是本地检测accessToken快过期的时候才使用,

一般本地存储的时候,也不叫refreshToken,前端可以取个别名,混淆代码让攻击者不能直接识别这个就是刷新令牌

缺点:前端每次请求需要判断token距离过期时间

优点:后端压力小,代码逻辑改动不大

刷新token方法未实现。

SpringBoot整合JWT实现登录认证

1、JWT的构成

- 头部(header):描述该JWT的最基本的信息,如类型以及签名所用的算法。

- 负载(payload):存放有效信息的地方。

- 签证(signature):base64加密后的header、base64加密后的payload和密钥secret加密后组成。

2、整合JWT

2.1 引入JWT依赖

com.auth0

java-jwt

3.18.3

2.2 编写JWTUtils工具类

package com.stock.utils;

import com.auth0.jwt.JWT;

import com.auth0.jwt.JWTCreator;

import com.auth0.jwt.JWTVerifier;

import com.auth0.jwt.algorithms.Algorithm;

import com.auth0.jwt.interfaces.DecodedJWT;

import com.auth0.jwt.interfaces.Verification;

import java.util.Calendar;

import java.util.Map;

public class JWTUtils {

private static final String SING="@#$%^*";

// 生成token

public static String getToken(Map map){

Calendar instance = Calendar.getInstance();

instance.add(Calendar.MINUTE,30);

//创建jwt builder

JWTCreator.Builder builder = JWT.create();

//payload

builder.withExpiresAt(instance.getTime());

map.forEach((k,v)-{

builder.withClaim(k,v);

});

//设置签名

String token = builder.sign(Algorithm.HMAC256(SING));

return token;

}

//验证令牌

public static void verifyToken(String token){

JWTVerifier require = JWT.require(Algorithm.HMAC256(SING)).build();

require.verify(token);

}

//获取token信息

public static DecodedJWT getTokenInfo(String token){

DecodedJWT verify = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);

return verify;

}

}

2.3 编写拦截器

public class JWTInterceptor implements HandlerInterceptor {

@Override

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

if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {

System.out.println("OPTIONS请求,放行");

return true;

}

HashMap map = new HashMap();

String token = request.getHeader("token");

try {

JWTUtils.verifyToken(token);

return true;

}catch (SignatureVerificationException e){

map.put("msg","无效签名!");

}catch (TokenExpiredException e){

map.put("msg","token过期!");

}catch (AlgorithmMismatchException e){

map.put("msg","token加密算法不一致");

}catch (Exception e){

map.put("msg","无效签名!");

}

map.put("state",404);

map.put("path","/login");

//将map转化为字符串返回给前端

String result = new ObjectMapper().writeValueAsString(map);

response.setContentType("application/json;charset=UTF-8");

response.getWriter().println(result);

return false;

}

}

注意:

1、token存放在请求的header中;

2、在前后端分离的项目中,发送的GET/POST请求实则为两次请求。第一次请求为OPTIONS请求,第二次请求才是GET/POST请求;在OPTIONS请求中,不会携带请求头的参数,会导致在拦截器上获取请求头为空,自定义的拦截器拦截成功。第一次请求不能通过,就不能获取第二次的请求。所以需要在拦截器中加上如下代码来判断是否为OPTIONS请求,对于OPTIONS请求直接放过。

if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {

System.out.println("OPTIONS请求,放行");

return true;

}

2.4 配置拦截器

package com.stock.config;

import com.stock.Interceptors.JWTInterceptor;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

public class IntercepterConfg implements WebMvcConfigurer {

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new JWTInterceptor())

.addPathPatterns("/**")

.excludePathPatterns("/login");

}

}

2.5 编写Controller

package com.stock.controller;

import com.stock.entity.User;

import com.stock.result.Result;

import com.stock.service.UserService;

import com.stock.utils.JWTUtils;

import com.stock.utils.ResultUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@RestController

public class LoginController {

private UserService userService;

@Autowired

public LoginController(UserService userService) {

this.userService = userService;

}

@PostMapping("/login")

public Result register(User user){

HashMap map = new HashMap();

map.put("username",user.getUserName());

String token = JWTUtils.getToken(map);

HashMap data = new HashMap();

data.put("token",token);

return ResultUtils.getresult(200,"登录成功!",data);

}

@GetMapping("/main")

public Result tomain(){

return ResultUtils.getresult(200,"访问成功",null);

}

}

2.6使用Postman测试

- 未登录前访问127.0.0.1:8888/main

- 先登录再访问127.0.0.1:8888/main

java jwt如何刷新过期时间

客户端

auth_header = JWT.encode({ user_id: 123, iat: Time.now.to_i, # 指定token发布时间 exp: Time.now.to_i + 2 # 指定token过期时间为2秒后,2秒时间足够一次HTTP请求,同时在一定程度确保上一次token过期,减少replay attack的概率;}, "my shared secret")

RestClient.get("", authorization: auth_header)

服务端

class ApiController ActionController::Base

attr_reader :current_user

before_action :set_current_user_from_jwt_token

def set_current_user_from_jwt_token

# Step 1:解码JWT,并获取User ID,这个时候不对Token签名进行检查

# the signature. Note JWT tokens are *not* encrypted, but signed.

payload = JWT.decode(request.authorization, nil, false) # Step 2: 检查该用户是否存在于数据库

@current_user = User.find(payload['user_id'])

# Step 3: 检查Token签名是否正确.

JWT.decode(request.authorization, current_user.api_secret)

# Step 4: 检查 "iat" 和"exp" 以确保这个Token是在2秒内创建的.

now = Time.now.to_i if payload['iat'] now || payload['exp'] now # 如果过期则返回401

end

rescue JWT::DecodeError

# 返回 401

endend

关于javajwtutil和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-12-11,除非注明,否则均为首码项目网原创文章,转载请注明出处。