上篇博文写了限制ip访问次数,这次写token认证。token认证必须是在用户登录之后,用jwt生成,然后将token信息存到response的消息头中来实现的。首先要放行登录的controller,未登录之前不需要token认证,登录之后再进行token认证。验证过程为:首先获取头信息,取出头信息中的内容。调用工具类获取出用户名,根据已登录的用户名集合判断是否是登录状态,登录的用户名集合是存放在redis中的。最后再判断token的过期时间。工具类我会在最下方贴出来
登录
@Autowired
private JwtTool jwtTool; // 工具类
@Autowired
private LoginService loginService // 登陆方法service层
@Autowired
private RedisTemplate redisTemplate; // redis
@Value("${jwt.header}")
private String header; // 读取配置文件
@Value("${jwt.tokenHead}")
private String tokenHead;
@RequestMapping("/login")
public ResponseBody login(UserPo userPo, HttpServletResponse response) {
ResponseBody responseBody = new ResponseBody();
String username = userPo.getUsername();
String password = userPo.getPassword();
if (StringUtils.isBlank(username)) { // 用户名为空
return DyTool.responseReturn(StatusEnum.USERNAMEISNULL); // (工具类)
}
if (StringUtils.isBlank(password)) { // 密码为空
return DyTool.responseReturn(StatusEnum.PASSWORDISNULL); // (工具类)
}
userPo = loginService.findUserByName(username);
if (!userPo.getPassword().equals(SecuritySHATool.md5Encrypt(password))) { // // (工具类)密码错误(MD5加密)
return DyTool.responseReturn(StatusEnum.PASSWORDERROR); //(工具类)
}
response.setHeader(header, tokenHead + jwtTool.generateToken(username)); // (工具类)
BoundSetOperations userSet = redisTemplate.boundSetOps("usernameSet");
userSet.add(username);
return DyTool.responseReturn(StatusEnum.LOGINSUCCESS); //(工具类)登录成功
}
验证token
@Autowired
private JwtTool jwtTool; // 工具类
@Value("${jwt.header}")
private String tokenHeader; // 读取的配置文件(自己写就可以)
@Value("${jwt.tokenHead}")
private String tokenHead
// 获取header中的token信息
String authHeader = httpServletRequest.getHeader(this.tokenHeader);
if (authHeader == null || !authHeader.startsWith(this.tokenHead)) { // 是否含有token的头信息
DyTool.responseWriter(StatusEnum.NOHEADER, httpServletResponse); //(工具类) 缺少头信息
return;
}
final String authToken = authHeader.substring(tokenHead.length()); // "Bearer "
String useraccount = jwtTool.getUserAccountFromToken(authToken); // (工具类)获取出token中的用户名
BoundSetOperations usernameSet = redisTemplate.boundSetOps("usernameSet"); // 取出redis中的在线用户名集合
logger.info("token验证过滤器[JwtAuthenticationTokenFilter]_用户名为:" + useraccount);
if (!usernameSet.isMember(useraccount)) { // 是否登录过
DyTool.responseWriter(StatusEnum.UNLOGIN, httpServletResponse); //(工具类) 未登录
return;
}
username = useraccount;
if (jwtTool.isTokenExpired(authToken)) { // (工具类)验证过期时间
DyTool.responseWriter(StatusEnum.TOKENOUTTIME, httpServletResponse); // (工具类)验证token失败
return;
}
工具类
/**
* @Description: JWT工具类,里面的方法未写成静态方法,需要注入
* @Author: 臧东运
* @CreateTime: 2019/4/17 13:58
*/
@Component
public class JwtTool {
private static final String CLAIM_KEY_USER_ACCOUNT = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret; //秘钥 // 读取配置文件,自己配置
@Value("${jwt.expiration}")
private Long expiration; //过期时间
/**
* 从token中获取用户account
* @param token
* @return
*/
public String getUserAccountFromToken(String token) {
String useraccount;
try {
final Claims claims = getClaimsFromToken(token);
useraccount = claims.getSubject();
} catch (Exception e) {
useraccount = null;
}
return useraccount;
}
/**
* 从token中获取创建时间
* @param token
* @return
*/
public Date getCreatedDateFromToken(String token) {
Date created;
try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
} catch (Exception e) {
created = null;
}
return created;
}
/**
* 获取token的过期时间
* @param token
* @return
*/
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
/**
* 从token中获取claims
* @param token
* @return
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生存token的过期时间
* @return
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 判断token是否过期
* @param token
* @return
*/
public Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
Boolean result= expiration.before(new Date());
return result;
}
/**
* 生成token
* @param usernmae
* @return
*/
public String generateToken(String usernmae) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USER_ACCOUNT, usernmae);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* token 是否可刷新
* @param token
* @return
*/
public Boolean canTokenBeRefreshed(String token) {
final Date created = getCreatedDateFromToken(token);
return !isTokenExpired(token);
}
/**
* 刷新token
* @param token
* @return
*/
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 验证token
* @param token
* @param usernmae
* @return
*/
public Boolean validateToken(String token, String usernmae) {
final String useraccount = getUserAccountFromToken(token);
final Date created = getCreatedDateFromToken(token);
Boolean result= (
useraccount.equals(usernmae)
&& !isTokenExpired(token)
);
return result;
}
}
/**
* @param statusEnum 状态枚举类
* @param response
* @return java.lang.String
* @Description: 发生错误时响应给请求者, 拦截器过滤器层面,需要HttpServletResponse
* @Author Zangdy
* @CreateTime 2019/4/22 11:09
*/
public static void responseWriter(StatusEnum statusEnum, HttpServletResponse response) throws IOException {
ResponseBody responseBody = new ResponseBody();
responseBody.setStatus(statusEnum.getStatus());
responseBody.setMessage(statusEnum.getMessage());
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(JSONObject.toJSONString(responseBody));
}
/**
* @param statusEnum
* @return java.lang.String
* @Description: 发生错误时响应给请求者, Controller层面,不需要HttpServletResponse
* @Author Zangdy
* @CreateTime 2019/4/22 11:09
*/
public static ResponseBody responseReturn(StatusEnum statusEnum){
ResponseBody responseBody = new ResponseBody();
responseBody.setStatus(statusEnum.getStatus());
responseBody.setMessage(statusEnum.getMessage());
return responseBody;
}
/**
* @Description: 状态枚举类
* @Author: 臧东运
* @CreateTime: 2019/4/22 10:53
*/
public enum StatusEnum {
UNLOGIN("0000","未登录"),
/**
* 状态 02 开头为成功
* */
LOGINSUCCESS("0201","登陆成功"),
LOGOUTSUCCESS("0202","注销成功"),
UPLOADSUCCESS("0203","文件上传成功"),
/**
* 状态 04 开头为失败
*/
IPBLOCKED("0400","因非法请求,已被禁止访问,请稍后重试!"),
ACCESSDENIED("0401","无权限访问"),
USERNAMEISNULL("0402","用户名不能为空!"),
PASSWORDISNULL("0402","密码不能为空!"),
USERNAMEERROR("0402","用户名错误!"),
PASSWORDERROR("0402","密码错误!"),
NOHEADER("0403","缺少头信息"),
TOKENOUTTIME("0404","token认证已过期"),
APPTIMESIGNISNULL("0405","sign认证参数为空"),
SIGNUNTRUE("0406","sign参数不正确"),
SIGNOUTTIME("0407","sign签名已过期"),
ERROR("0499","服务器异常,请稍后重试。如有问题,请联系管理员"),
CUSTOM("0500","自定义异常");
private String status;
private String message;
StatusEnum(String status, String message) {
this.status = status;
this.message = message;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
版权声明:本文为qq_39554240原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。