token分平台设置方法
本文介绍了Spring下的登录和鉴权机制的主要方法以及 token认证的主要流程,并介绍在spring中web端和APP端设置不同token过期时间的实现方法。主要基于SpringBoot+springSecurity+JWT框架实现。
一、应用场景
同一系统的跨平台操作,基于用户习惯,web端和app端用户使用时间长短常常不同,统一过长时间容易造成服务器资源浪费,统一过短使得用户未操作完就登录过期。因此,为更便于用户使用,分平台设置token过期时间能提升用户体验。
二、登录方法和token鉴权
要分平台设置token过期时间,首先要了解SpringSecurity登录流程的主要方法和token生成。
1、登录流程
登录-->校验用户名、密码、验证码-->redis存储登录用户信息-->生成token(JWT)-->返回token
// 仅展示关键语句
java
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid(),loginBody.getClientPubKey(), loginBody.getPlatForm());
ajax.put(Constants.TOKEN, token);
return ajax;
}
java
public String login(String username, String aes_password, String code, String uuid, String clientPubKey, String platForm) {
// 验证用户名密码
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token
loginUser.setPlatForm(platForm);
return tokenService.createToken(loginUser);
}
2、JWT
JWT是一种基于 Token 的认证授权机制, 可用于创建token。
Token = Head+info+sign
Head: 编码方式
Info:用户信息,包括用户名等自定义信息
Sign:签名
如下所示:
java
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
claims.put(Constants.JWT_USERID, loginUser.getUserId());
claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
private String createToken(Map<String, Object> claims)
{
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
3、Token鉴权
登录后返回的token存于前端缓存,每次请求时放于请求头,经过拦截器时解析token,并verifyToken方法校验token是否有效或过期,同时redreshToken延长过期时间(本次为活跃)。
// 校验
java
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if(loginUser.getPlatForm().equals("pc")){
if (expireTime - currentTime <= MILLIS_MINUTE_TEN_PC)
{
refreshToken(loginUser);
}
}else if(loginUser.getPlatForm().equals("app")) {
if (expireTime - currentTime <= MILLIS_MINUTE_TEN_APP) {
refreshToken(loginUser);
}
}
}
// 更新过期时间
java
public void refreshToken(LoginUser loginUser)
{
if(loginUser.getPlatForm().equals("pc")){
expireTime = pcExpireTime;
}else if(loginUser.getPlatForm().equals("app")){
expireTime = appExpireTime;
}
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
三、实现方法
1、配置文件
Pc端过期时间59min,app端3天
java
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认59分钟; APP端3天)
expireTime:
defaultExpireTime: 59
pcExpireTime: 59
appExpireTime: 4320
2、登录信息实体类
增加平台信息
src/main/java/com/common/core/domain/model/LoginBody.java
src/main/java/com/common/core/domain/model/LoginUser.java
java
public class LoginBody {
// ****其他省略
/**
* 登录平台: 手机端='app',PC端='pc'
*/
private String platForm;
public String getPlatForm() {
return platForm;
}
public void setPlatForm(String platForm) {
this.platForm = platForm;
}
}
3、登录方法
(1)login的controller层方法
生成token的方法参数加上平台信息
src/main/java/com/web/controller/system/SysLoginController.java
java
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid(),loginBody.getClientPubKey(), loginBody.getPlatForm());
ajax.put(Constants.TOKEN, token);
return ajax;
}
(2) 登录信息检验及token生成
src/main/java/com/inspur/framework/web/service/SysLoginService.java
// 基于SpringSecurity的验证方法,修改返回的登录用户信息,可以在返回后再人工设置。
java
public String login(String username, String aes_password, String code, String uuid, String clientPubKey, String platForm) {
// 仅仅展示重要关键语句
// 验证用户名密码
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
// 返回登录信息
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token
loginUser.setPlatForm(platForm);
return tokenService.createToken(loginUser);
}
java
private String createToken(Map<String, Object> claims)
{
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
(3)Token验证鉴权及更新
src/main/java/com/inspur/common/service/TokenService.java
// 用户每次请求将token信息存放于请求头,经过拦截器拦截。
java
@Component
public class TokenService
{
// 令牌有效期(默认30分钟)
@Value("${token.expireTime.defaultExpireTime}")
private int expireTime;
@Value("${token.expireTime.pcExpireTime}")
private int pcExpireTime;
@Value("${token.expireTime.appExpireTime}")
private int appExpireTime;
//pc端-距离20分钟时刷新token过期时间
private static final Long MILLIS_MINUTE_TEN_PC = 20 * 60 * 1000L;
//app端-距离1天时刷新token过期时间
private static final Long MILLIS_MINUTE_TEN_APP = 24 * 60 * 60 * 1000L;
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if(loginUser.getPlatForm().equals("pc")){
if (expireTime - currentTime <= MILLIS_MINUTE_TEN_PC)
{
refreshToken(loginUser);
}
}else if(loginUser.getPlatForm().equals("app")) {
if (expireTime - currentTime <= MILLIS_MINUTE_TEN_APP) {
refreshToken(loginUser);
}
}
}
java
public void refreshToken(LoginUser loginUser)
{
if(loginUser.getPlatForm().equals("pc")){
expireTime = pcExpireTime;
}else if(loginUser.getPlatForm().equals("app")){
expireTime = appExpireTime;
}
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
}
4、前端传递平台信息
(1)web端(基于Vue)
登录传递平台信息:platForm='pc'
src/store/modules/user.js
javascript
// 登录
Login({commit}, userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
const platForm = 'pc'
return new Promise((resolve, reject) => {
getPublicKey(username).then(res => {
if (res.code === 200) {
let result = encryptData(res.data, password);
let aes_password = result.encryptedData;
login(username, aes_password, code, uuid,result.clientKey,platForm).then(res => {
setToken(res.token)
commit('SET_TOKEN', res.token)
resolve()
}).catch(error => {
reject(error)
})
}
})
})
},
src/api/login.js
javascript
export function login(username, password, code, uuid,clientPubKey) {
const platForm = 'pc'
const data = {
username,
password,
code,
uuid,
clientPubKey,
platForm
}
return request({
url: '/login',
method: 'post',
data: data
})
}
(2)app端(基于uniapp)
api/login.js
javascript
// 登录方法
export function login(username, password, code, uuid) {
let platForm = 'app'
const data = {
username,
password,
code,
uuid,
platForm
}
return request({
'url': '/appLogin',
headers: {
isToken: false
},
'method': 'post',
'data': data
})
}