权限验证实现方式
-
session
1.实现方案:session+filter
2.实现过程:URL/*->filter->通过URL判断是否是登录页,是,则放行。->不是,判断session中是否存在用户->不存在,则重定向到登录页;存在,放行->退出,注销session。 -
cookie
1.实现方案:session+filter
2.实现过程:和session一样 -
token
1.实现方案:token+拦截器
2.实现过程:URL/*->过滤器(可以自定义两个注解,一个放行注解,一个需要token验证注解)->是登录页,验证登录是否成功->否,重新登录;是,则生成token->传回给前段,可以使用cookie或响应头中->不是登录页,从请求头中获取token->token不存在,重新登录;token存在但过期,重新登录;token存在且不过去且正确->放行
3.token验证方案:1.后端将生成的token放在Redis中,下次取出比较;2.jwt方式做签名认证,比较签名是否被修改即可,token是否超时
4.token在请求时的存放地方:1.作为传入参数;2.放在cookie中;3.放在请求头中,也是最常见的 -
超时比较
1.session超时(一段时间内无操作)后,退出。 session.setMaxInactiveInterval(60);
2.cookie超时后也会退出。cookie.setMaxAge(300);res.addCookie(cookie);
3.session超时,如3min,则指的是3min内无任何操作,就认为超时会退出;如果任意3min内一直有操作,则永不超时退出。如果浏览器关闭,则session生命周期结束,相当于退出了。
4.cookie超时,如3min,则指的是session创建后,生产cookie的那一刻起,过了3min后cookie就超时了。
5.如果要实现关闭浏览器后在访问页面,仍不用登录即可直接访问,需用到cookie。但cookie时间设置不当,会出现,正在访问页面,突然退出,要重新登录才行。如果从这个角度出发,当cookie和session都存在时候,cookie的超时时间应该设置无限大。
6.如果cookie没有设置超时时间,其生命周期和会话周期绑定,存在浏览器内存中。浏览器关闭,cookie就消失。如果设置超时时间,则存在硬盘上。 -
token实现方案具体代码
拦截器代码
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
TokenUtil tokenUtil;
public static final String SECRET = "123456";
private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
// 检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
String token = request.getHeader("token");
Result result = new Result(ResultEnum.TOKEN_TIMEOUT_OR_WRONG);
// 没有token
if (StringUtils.isEmpty(token)) {
result.setMsg("no token,need login");
response.getWriter().print(JSON.toJSONString(result));
logger.error("no token,need login");
return false;
}
// 判断token是否过期,是否正确
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (TokenExpiredException e) {
logger.error("token超时:");
result.setMsg("token is out time");
response.getWriter().print(JSON.toJSONString(result));
return false;
} catch (SignatureVerificationException e) {
logger.error("token签名异常:");
result.setMsg("token is wrong");
response.getWriter().print(JSON.toJSONString(result));
return false;
}
int userid = jwt.getClaims().get("userId").asInt();
request.setAttribute("userid", userid);
}
}
// String urlPath = request.getRequestURI();
// System.out.println("urlPath:," + urlPath);
// if (!urlPath.contains("login")) {
// String token = request.getHeader("token");
// Result result = new Result(ResultEnum.FAILED);
// if (StringUtils.isEmpty(token)) {
// result.setMsg("no token,need login");
// response.getWriter().print(JSON.toJSONString(result));
// logger.error("no token,need login");
// return false;
// } else {
// DecodedJWT jwt = null;
// try {
// JWTVerifier verifier = JWT.require(Algorithm.HMAC256("qxmz")).build();
// jwt = verifier.verify(token);
// } catch (TokenExpiredException e) {
// logger.error("token超时:");
// result.setMsg("token is out time");
// response.getWriter().print(JSON.toJSONString(result));
// return false;
// } catch (SignatureVerificationException e) {
// logger.error("token签名异常:");
// result.setMsg("token is wrong");
// response.getWriter().print(JSON.toJSONString(result));
// return false;
// }
// int userid = jwt.getClaims().get("userId").asInt();
// request.setAttribute("userid", userid);
// System.out.println("userId:" + userid);
// }
// }
return true;
}
}
token工具类代码
@Component
public class TokenUtil {
@Value("${token.expire_time}")
private long EXPIRE_TIME;
public static final String SECRET = "123456";
private static final Logger logger = LoggerFactory.getLogger(TokenUtil.class);
/**
* 创建token
*
* @author junzhang19
* @since 2020年7月24日下午2:00:45
* @param userId
* @return
*/
public String creatToken(int userId) {
String token = null;
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME * 1000);
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 附带userId信息
token = JWT.create().withClaim("userId", userId).withExpiresAt(date).sign(algorithm);
} catch (JWTCreationException e) {
// 如果Claim不能转换为JSON,或者在签名过程中使用的密钥无效,那么将会抛出JWTCreationException异常。
logger.error("token创建异常:{}", e);
}
return token;
}
public DecodedJWT verifyToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (TokenExpiredException e) {
logger.error("token超时:{}", e);
} catch (SignatureVerificationException e) {
logger.error("token签名异常:{}", e);
}
return jwt;
}
}
需要token及放行,对应的两个注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
参考文档:
https://www.jianshu.com/p/e88d3f8151db
https://www.jianshu.com/p/0fed399c2561