session 方式在分布式环境下不好用,jwt(json web token)是目前主流的无状态认证方案。服务端不需要存 session,token 中包含了用户信息。
一、jwt 结构
header.payload.signature header: 加密算法 payload: 用户数据(不要放密码) signature: 签名,防止篡改
二、jwt 工具类
@component
public class jwtutil {
@value("${jwt.secret:mysecretkey}")
private string secret;
@value("${jwt.expire:86400000}")
private long expire; // 默认24小时
public string generatetoken(long userid, string username) {
date now = new date();
return jwts.builder()
.setsubject(userid.tostring())
.claim("username", username)
.setissuedat(now)
.setexpiration(new date(now.gettime() + expire))
.signwith(signaturealgorithm.hs256, secret)
.compact();
}
public claims parsetoken(string token) {
return jwts.parser()
.setsigningkey(secret)
.parseclaimsjws(token)
.getbody();
}
public boolean validatetoken(string token) {
try {
parsetoken(token);
return true;
} catch (exception e) {
return false;
}
}
}
三、登录接口
@restcontroller
@requestmapping("/auth")
public class authcontroller {
@autowired
private jwtutil jwtutil;
@postmapping("/login")
public resultvo<map<string, object>> login(
@requestparam string username,
@requestparam string password) {
// 校验用户名密码
if (!"admin".equals(username) || !"123456".equals(password)) {
return resultvo.error(401, "用户名或密码错误");
}
// 生成 token
string accesstoken = jwtutil.generatetoken(1001l, username);
string refreshtoken = jwtutil.generaterefreshtoken(1001l);
map<string, object> result = new hashmap<>();
result.put("accesstoken", accesstoken);
result.put("refreshtoken", refreshtoken);
result.put("expiresin", 86400);
return resultvo.success(result);
}
}
四、token 刷新
@postmapping("/refresh")
public resultvo<map<string, object>> refresh(
@requestparam string refreshtoken) {
if (!jwtutil.validatetoken(refreshtoken)) {
return resultvo.error(401, "refreshtoken 无效");
}
claims claims = jwtutil.parsetoken(refreshtoken);
long userid = long.parselong(claims.getsubject());
string username = claims.get("username", string.class);
string newaccesstoken = jwtutil.generatetoken(userid, username);
string newrefreshtoken = jwtutil.generaterefreshtoken(userid);
return resultvo.success(map.of(
"accesstoken", newaccesstoken,
"refreshtoken", newrefreshtoken
));
}
五、拦截器校验
@component
public class jwtinterceptor implements handlerinterceptor {
@autowired
private jwtutil jwtutil;
@override
public boolean prehandle(httpservletrequest request,
httpservletresponse response,
object handler) throws exception {
string token = request.getheader("authorization");
if (token == null || !token.startswith("bearer ")) {
throw new businessexception(401, "未登录");
}
token = token.substring(7);
if (!jwtutil.validatetoken(token)) {
throw new businessexception(401, "token 已过期");
}
// 将用户信息存入请求上下文
claims claims = jwtutil.parsetoken(token);
request.setattribute("userid", claims.getsubject());
request.setattribute("username", claims.get("username"));
return true;
}
}
六、前端调用
// 登录
const resp = await fetch('/auth/login', {
method: 'post',
headers: {'content-type': 'application/json'},
body: json.stringify({username: 'admin', password: '123456'})
});
const { accesstoken, refreshtoken } = await resp.json();
// 后续请求携带 token
fetch('/api/user', {
headers: {'authorization': 'bearer ' + accesstoken}
});
// token 过期时用 refreshtoken 刷新
async function refreshaccesstoken() {
const resp = await fetch('/auth/refresh', {
method: 'post',
body: new urlsearchparams({refreshtoken})
});
const data = await resp.json();
accesstoken = data.accesstoken;
}
七、安全注意事项
// 1. jwt 中不要放敏感信息
claims claims = jwts.parser()
.setsigningkey(secret)
.parseclaimsjws(token)
.getbody();
string password = claims.get("password"); // ❌ 错误!
// 2. 密钥不要写死,用配置中心或环境变量
@value("${jwt.secret}")
private string secret;
// 3. accesstoken 有效期短(15-30分钟)
// refreshtoken 有效期长(7天),专门用来刷新 accesstoken
总结
session 认证:服务端存 session,分布式下需要共享 session jwt 认证: 无状态,token 携带用户信息,适合分布式 jwt 缺点:签发后无法主动失效(只能等过期) 解决方案:accesstoken 短时效 + refreshtoken 刷新机制
到此这篇关于springboot整合jwt实现token认证与自动刷新的文章就介绍到这了,更多相关springboot token认证与自动刷新内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论