包含javajwtutil的词条
本篇文章给大家谈谈javajwtutil,以及对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、JWT的TOKEN续期功能
- 2、如何在Java 中创建和验证JWT
- 3、JWT token封装以及自动刷新方案建议
- 4、SpringBoot整合JWT实现登录认证
- 5、java jwt如何刷新过期时间
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和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
发布于:2022-12-11,除非注明,否则均为
原创文章,转载请注明出处。