使用redis缓存登录信息,生成一个唯一随机token 作为 redis的key,然后将token 存储到 Jwts 对象中,生成新的jwtToken值返回给前台。项目访问时携带该 token,先进行JWT解析得到存储的随机token到redis进行查找登录对象数据。
1、创建自己的Token 管理类
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.jrf.common.constants.Constants;
import com.jrf.common.ip.AddressUtils;
import com.jrf.common.ip.IpUtils;
import com.jrf.common.lang.StringUtils;
import com.jrf.core.exception.user.AuthenticationException;
import com.jrf.core.redis.RedisCache;
import com.jrf.core.web.domain.model.LoginUser;
import com.jrf.core.web.util.ServletUtils;
import cn.hutool.core.util.IdUtil;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
* 管理端token验证处理
*/
@Component
public class TokenService {
// 令牌自定义标识
@Value("${token.header}")
private String header;
// 令牌秘钥
@Value("${token.secret}")
private String secret;
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;
protected static final long MILLIS_SECOND = 1000;
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
@Autowired
private RedisCache redisCache;
/**
* 登录创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public LoginUser createToken(LoginUser loginUser) {
String token = IdUtil.simpleUUID();
loginUser.setToken(token);
// 设置浏览器信息
setUserAgent(loginUser);
// 设置用户到期时间 同时 缓存用户信息
refreshToken(loginUser);
//将token存储,使用JWT生成新的token信息
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
String jwtToken = createJwtToken(claims);
loginUser.setJwtToken(jwtToken);
return loginUser;
}
/**
* 访问认证用户身份信息 同时刷新令牌时间
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request) throws AuthenticationException{
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
//解析JWTtoken信息
Claims claims = parseToken(token);
// 获得真实的用户token信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
//获取缓存的key值
String userKey = getTokenKey(uuid);
//从缓存中获取用户
LoginUser user = redisCache.getCacheObject(userKey);
if(user!=null) {
//验证token时效,低于20分钟则重新刷新令牌
verifyToken(user);
return user;
}
} catch (Exception e) {
e.printStackTrace();
}
}
throw new AuthenticationException("用户认证失效,需要重新登录");
}
/**
* 退出登录
*
* @param request
*/
public void logout(HttpServletRequest request, Long userId) {
// 获取请求携带的令牌
String token = getToken(request);
delLoginUser(token);
}
/**
* 设置用户身份信息
*/
// private void setLoginUser(LoginUser loginUser) {
// if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
// refreshToken(loginUser);
// }
// }
/**
* 删除用户身份信息
*/
private void delLoginUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
}
/**
* 验证令牌有效期,相差不足20分钟,自动刷新缓存
*
* @param loginUser
* @return 令牌
*/
private void verifyToken(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
private void refreshToken(LoginUser loginUser) {
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);
}
/**
* 设置用户代理信息
*
* @param loginUser 登录信息
*/
private void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOperatingSystem().getName());
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createJwtToken(Map<String, Object> claims) {
Date now = Calendar.getInstance().getTime();
String token = Jwts.builder().setClaims(claims).setIssuedAt(now).signWith(SignatureAlgorithm.HS512, secret)
.setExpiration(new Date(System.currentTimeMillis() + expireTime * 60 * 1000)).compact();
return token;
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims parseToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
// private String getUsernameFromToken(String token) {
// Claims claims = parseToken(token);
// return claims.getSubject();
// }
/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}
private String getTokenKey(String uuid) {
return Constants.LOGIN_TOKEN_KEY + uuid;
}
}
2、登录对象实体
import java.io.Serializable;
import java.util.Set;
import com.jrf.module.entity.SysUser;
/**
* 管理登录用户
*/
public class LoginUser implements Serializable{
/**
*
*/
private static final long serialVersionUID = 8200691872337648784L;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
private String jwtToken;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SysUser getUser() {
return user;
}
public void setUser(SysUser user) {
this.user = user;
}
public String getJwtToken() {
return jwtToken;
}
public void setJwtToken(String jwtToken) {
this.jwtToken = jwtToken;
}
public LoginUser() {
}
public LoginUser(SysUser user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
}
3、登录业务类
/**
* 登录业务
*
*/
@Component
public class LoginService {
private static final Logger log = LoggerFactory.getLogger(LoginService.class);
@Autowired
private TokenService tokenService;
@Autowired
private RedisCache redisCache;
@Autowired
private SysLogininforService sysLogininforService;
@Autowired
private SysUserService sysUserService;
@Autowired
private SysPermissionService sysPermissionService;
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public LoginUser login(HttpServletRequest request, String username, String password, String code, String uuid) {
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null) {
saveSysLogininfor(request, username, Constants.LOGIN_FAIL,"验证码已失效");
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
saveSysLogininfor(request, username, Constants.LOGIN_FAIL,"验证码错误");
throw new CaptchaException();
}
SysUser user = sysUserService.findByUsername(username);
if (StringUtils.isNull(user)) {
log.info("登录用户:{} 不存在.", username);
throw new BusinessException("用户名或密码不正确");
// } else if (user.getDelFlag().intValue() == 1) {
// log.info("登录用户:{} 已被删除.", username);
// throw new BusinessException("对不起,您的账号:" + username + " 已被删除");
} else if (user.getState().intValue() == 0) {
log.info("登录用户:{} 已被停用.", username);
throw new BusinessException("对不起,您的账号:" + username + " 已停用");
}else if(!Md5Utils.hash(password).equals(user.getPassword())) {
log.info("登录用户:{} 密码不正确.", username);
throw new BusinessException("用户名或密码不正确");
}
//存储登录日志
saveSysLogininfor(request, username, Constants.LOGIN_SUCCESS, "登录成功");
//设置授权信息
Set<String> permissions = doAuthorization(user);
LoginUser loginUser = new LoginUser(user, permissions);
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 登录出操作
* @param request
* @param userId
*/
public void logout(HttpServletRequest request,Long userId) {
//删除缓存
sysPermissionService.delPermsCache(userId);
sysPermissionService.delRolesCache(userId);
tokenService.logout(request,userId);
}
/**
* 获取用户权限授权操作 并缓存到redis中
* @param user
* @return
*/
public Set<String> doAuthorization(SysUser user) {
return sysPermissionService.getUserPerms(user);
}
private void saveSysLogininfor(HttpServletRequest request, String username, String status, String message) {
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(request);
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(LogUtils.getBlock(ip));
s.append(address);
s.append(LogUtils.getBlock(username));
s.append(LogUtils.getBlock(status));
s.append(LogUtils.getBlock(message));
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUsername(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
logininfor.setStatus(Constants.SUCCESS);
} else if (Constants.LOGIN_FAIL.equals(status)) {
logininfor.setStatus(Constants.FAIL);
}
sysLogininforService.insert(logininfor);
}
}