一、准备
- 依赖
<!--jwt依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.7</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
</dependency>
- 使用Md5工具对数据库密码加密
@Slf4j
public class Md5Utils {
public static String getMD5String(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
return new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("{}计算md5异常:{}",stackTraceElement,e.getMessage());
return null;
}
}
}
- 生成rsa的私钥和公钥,可以在线生成,不要空格不要换行,不要其他的字符
- 从数据库获取用户名和使用md5加密的密码
使用redis存储token,并且一个用户只允许有一个token
@Override
public ResultVo login(ManagerDto managerDto) {
if (ObjectUtils.isEmpty(managerDto)){
return new ResultVo().failure(ResultCode.PARAMETER_INVALID);
}
try {
String userName = managerDto.getUserName();
String password = managerDto.getPassword();
IdleManagerEntity manager = managerMapper.getManager(userName);
if (manager == null){
log.info("用户名为空");
return new ResultVo().failure(ResultCode.USER_NULL);
}
String md5String = Md5Utils.getMD5String(password);
if (!md5String.equals(manager.getMd5Password())){
log.info("密码错误");
return new ResultVo().failure(ResultCode.PASSWORD_ERROR);
}
manager.setMd5Password("");
try {
String effective = tokenEffectiveUtils.isEffective(userName);
ClassPathResource classPathResource = new ClassPathResource("rsa_pri");
InputStream inputStream = classPathResource.getInputStream();
PrivateKey privateKey = RsaUtils.getPrivateKey(ResourceStream.readBytes3(inputStream));
String token = JwtUtils.generateTokenExpireInMinutes(manager, privateKey, timeOut);
if (StringUtils.isBlank(effective)){
redisUtils.set(userName,token,timeOut);
log.info("{} create new token : {},timeout:{} min",userName,token,timeOut);
}else {
redisUtils.delete(userName);
redisUtils.set(userName,token,timeOut);
log.info("{} update token:{},timeOut:{} min",userName,token,timeOut);
}
log.info(" {} already login",userName);
return new ResultVo().success(token);
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("{} token error:{}",stackTraceElement,e.getMessage());
return new ResultVo().failure(ResultCode.SYSTEM_ERROR);
}
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("登录异常");
return new ResultVo().failure(ResultCode.SYSTEM_ERROR);
}
}
二、需要对请求方法进行拦截,判断是否携带token或者是否过期
从redis中获取token,判断当前携带的token是否与redis的token相同
package com.idle.idlemanager.config;
import com.idle.idlemanager.entity.dataobject.IdleManagerEntity;
import com.idle.idlemanager.enums.ResultCode;
import com.idle.idlemanager.util.JwtUtils;
import com.idle.idlemanager.util.RsaUtils;
import com.idle.idlemanager.util.TokenEffectiveUtils;
import com.idle.idlemanager.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.PublicKey;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private TokenEffectiveUtils tokenEffectiveUtils;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String method = request.getMethod();
if(method.equals("OPTIONS")){
return true;
}
String token = request.getHeader("token");
PublicKey publicKey = RsaUtils.getPublicKey(
ResourceUtils.getFile("src/main/resources/rsa_pub").getPath());
try {
IdleManagerEntity manager = (IdleManagerEntity) JwtUtils.getInfoFromToken(token, publicKey, IdleManagerEntity.class);
if (!token.equals(tokenEffectiveUtils.isEffective(manager.getUserName()))){
response.sendRedirect(request.getContextPath()+"/result");
return false;
}
return true;
}catch (Exception e){
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("{}token error:{}",stackTraceElement,e.getMessage());
}
response.sendRedirect(request.getContextPath()+"/result");
return false;
}
}
@RequestMapping("result")
public ResultVo result(){
return new ResultVo().failure(ResultCode.TOKEN_OVERDUE);
}
三、需要将拦截器注册到springmvc中,放行登录请求
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.excludePathPatterns("/login")
.addPathPatterns("/**");
}
}
四、前端配置拦截器
service.interceptors.request.use(
config => {
config.headers.token = sessionStorage.getItem("token");
return config
}
)
- 配置响应拦截器,根据后端“result”返回的错误信息验证token是否过期
service.interceptors.response.use(
response => {
if(response.data.code == 'A0004'){
sessionStorage.removeItem("token")
location.href = '/login'
return ;
}
return response.data
}
)
五、贴上公钥私钥吧
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
六、redis工具类
package com.idle.idlemanager.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String get(final String key) {
return redisTemplate.opsForValue().get(key);
}
public boolean set(final String key, String value,Integer timeOut) {
boolean result = false;
try {
redisTemplate.opsForValue().set(key, value,timeOut, TimeUnit.MINUTES);
result = true;
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("{} redis set error:{}",stackTraceElement,e.getMessage());
}
return result;
}
public boolean getAndSet(final String key, String value) {
boolean result = false;
try {
redisTemplate.opsForValue().getAndSet(key, value);
result = true;
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("redis update error",stackTraceElement,e.getMessage());
}
return result;
}
public boolean delete(final String key) {
boolean result = false;
try {
redisTemplate.delete(key);
result = true;
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("redis delete error",stackTraceElement,e.getMessage());
}
return result;
}
}
七、判断当前用户是否过期
package com.idle.idlemanager.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Slf4j
@Component
public class TokenEffectiveUtils {
@Autowired
private RedisUtils redisUtils;
public String isEffective(String managerName){
String s = "";
try {
s = redisUtils.get(managerName);
} catch (Exception e) {
StackTraceElement stackTraceElement = e.getStackTrace()[0];
log.error("{} judge managermanent token effective error:{}",stackTraceElement,e.getMessage());
}
return s;
}
}