使用Token 和 JWT机制实现 登录认证

使用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);
	}
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值