说明

  • 老项目和新框架项目中用户认证的密码算法不同
  • 老项目中使用 cookie + spring session + redis 方式实现会话管理;新框架中采用 jwt + redis 方式
  • 现在统一为新框架方式 ,修改老项目中相关内容

步骤

pom.xml

1
2
3
4
5
6
<!--引入 jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>

JwtUtil.java

封装 jwt 工具类,实现 token 的生成和解析等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package com.bjtcrj.scm.system.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.bjtcrj.scm.common.exception.ApplicationRuntimeException;
import com.bjtcrj.scm.common.utils.SpringContextUtil;
import com.bjtcrj.scm.common.utils.oConvertUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;

/**
* @Author Scott
* @Date 2018-07-12 14:23
* @Desc JWT工具类
**/
public class JwtUtil {

// Token过期时间30分钟(用户登录过期时间是此时间的两倍,以token在reids缓存时间为准)
public static final long EXPIRE_TIME = 30 * 60 * 1000;
public static final String X_ACCESS_TOKEN = "X-Access-Token";

/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String loginname, String secret) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).withClaim("loginname", loginname).build();
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}

/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getLoginname(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("loginname").asString();
} catch (JWTDecodeException e) {
return null;
}
}

/**
* 生成签名,5min后过期
*
* @param loginname 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String loginname, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create().withClaim("loginname", loginname).withExpiresAt(date).sign(algorithm);
}

/**
* 根据request中的token获取用户账号
*
* @param request
* @return
*/
public static String getUserNameByToken(HttpServletRequest request) throws ApplicationRuntimeException {
String accessToken = getTokenByRequest(request);
String loginname = getLoginname(accessToken);
if (oConvertUtils.isEmpty(loginname)) {
throw new ApplicationRuntimeException("未获取到用户");
}
return loginname;
}

/**
* 获取 request 里传递的 token
*
* @param request
* @return
*/
public static String getTokenByRequest(HttpServletRequest request) {
String token = request.getParameter("token");
if (token == null) {
token = request.getHeader(X_ACCESS_TOKEN);
}
return token;
}

/**
* 从session中获取变量
* @param key
* @return
*/
public static String getSessionData(String key) {
//${myVar}%
//得到${} 后面的值
String moshi = "";
if(key.indexOf("}")!=-1){
moshi = key.substring(key.indexOf("}")+1);
}
String returnValue = null;
if (key.contains("#{")) {
key = key.substring(2,key.indexOf("}"));
}
if (oConvertUtils.isNotEmpty(key)) {
HttpSession session = SpringContextUtil.getHttpServletRequest().getSession();
returnValue = (String) session.getAttribute(key);
}
//结果加上${} 后面的值
if(returnValue!=null){returnValue = returnValue + moshi;}
return returnValue;
}

public static void main(String[] args) {
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbm5hbWUiOiJhZG1pbiIsImV4cCI6MTYzNDI2NjI2NX0.Z1B3P0U7zNxc2579TvFHJ49PR9V5-TJfFNeObIXLf6g";
System.out.println(JwtUtil.getLoginname(token));
}
}

登录认证通过后生成 token,存入 Redis 并返回前端

1
2
3
4
5
6
// 生成token
String token = JwtUtil.sign(loginname, userpassword);
// 设置token缓存有效时间
redisCacheManager.set(CommonConstant.PREFIX_USER_TOKEN + token, token, JwtUtil.EXPIRE_TIME*2 / 1000);
// redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);
j.setObj(token);

前端请求时头部添加 token 信息

后端解析头部 token,并做校验、解析出 loginname

1
2
3
String loginname = JwtUtil.getUserNameByToken(request);

String loginname = JwtUtil.getUsername(token);

其它

用户表字段修改

1
2
3
4
5
6
7
8
9
alter table SCM_USER modify PASSWORD VARCHAR2(255)
/

alter table SCM_USER modify PASSWORDDOWN VARCHAR2(255)
/

alter table SCM_USER
add SALT varchar2(20)
/

User.java 、UserDto.java 扩展 salt

1
2
3
4
/**
* md5密码盐
*/
private String salt;

UserDao.xml 插入语句添加 salt

1
2
3
4
5
resultMap、Base_Column_List 添加

insertSelective 中添加

updatePwdBasicByDto 中临时添加-批量更新使用,后删除即可

UserService.java getByLoginname返回类型修改

PasswordUtil.java

SpringContextUtil.java

oConvertUtils.java

LoginController.java

Constants.java

1
2
3
public static final String X_ACCESS_TOKEN = "X-Access-Token";
/** 登录用户Token令牌缓存KEY前缀 */
public static final String PREFIX_USER_TOKEN = "prefix_user_token_";

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
* 初始化用户密码和盐-临时使用-后删除
* */
@RequestMapping(value = "/initUserData")
public void initUserData(HttpServletRequest request, HttpServletResponse response) throws Exception {
List<UserDto> userList = userService.getUserList(new UserDto());
for (UserDto userDto : userList) {
String salt = oConvertUtils.randomGen(8);
userDto.setSalt(salt);
String passwordEncode = PasswordUtil.encrypt(userDto.getLoginname(), "123456", salt);
userDto.setPassword(passwordEncode);
userService.UpdateBasicPwd(userDto);
}

Json j = new Json();
j.setSuccess(true);
this.writeJson(j,request, response);
}

login.jsp

1
2
//登录成功后保存 token
localStorage.setItem("token", result.obj); //token 信息