授权与认证之jwt(四)创建OAuth2 Filter类
接上篇讲到授权与认证之jwt(三)刷新令牌该如何设计 这篇文章中要注意
注意事项:
因为在OAuth2 Filter类中要读写
ThreadLocal中的数据,所以OAuth:2 Filter类
必须要设置成多例的,否则ThreadLocal将无法使用。
一、在配置文件中,添加)WT需要用到的密钥、过期时间和缓存过期时间。
emos:
jwt:
#密钥
secret: abc123456
#令牌过期时间(天)
expire: 5
#令牌缓存时间(天)
cache-expire: 10
二、创建OAuth2Filter类
@Component
@Scope("prototype")
public class OAuth2Filter extends AuthenticatingFilter {
@Autowired
private ThreadLocalToken threadLocalToken;
@Value("${emos.jwt.cache-expire}")
private int cacheExpire;
@Autowired
private JwtUtil jwtUtil;
//用于往redis中存取数据
@Autowired
private RedisTemplate redisTemplate;
/**
* 拦截请求后,用于把令牌字符串封装成令牌对象
*/
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest req=(HttpServletRequest)request;
String token = getRequestToken(req);
if (StrUtil.isBlank(token)) {
return null;
}
return new OAuth2Token(token);
}
/**
*判断哪种请求可以被shiro处理
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue){
HttpServletRequest req = (HttpServletRequest)request;
//ajax提交application/json数据的时候,会先发出Option请求
//这里要放行Option请求,不需要shiro处理
if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
return true;
}
//除了Options请求之外,所以请求都要被shiro处理
return false;
}
/**
* 该方法用于处理所有应该被shiro处理的请求
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse)servletResponse;
//允许跨域请求
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setContentType("text/html;charset=utf-8");
threadLocalToken.clear();
String token = getRequestToken(req);
if (StrUtil.isBlank(token)) {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.getWriter().print("无效令牌");
return false;
}
//验证token是否内容有效,验证是否过期
try {
jwtUtil.verifyToken(token);
}catch (TokenExpiredException e){
//查redis有没有该令牌
if (redisTemplate.hasKey(token)) {
//有数据,需要删掉缓存的老令牌,重新生成新令牌
redisTemplate.delete(token);
int userId=jwtUtil.getUserId(token);
//创建新token
token=jwtUtil.createToken(userId);
//把token存到redis中
redisTemplate.opsForValue().set(token,userId+"",cacheExpire, TimeUnit.DAYS);
//把token存到threadLocal中
threadLocalToken.setToken(token);
}else {
//客户端和redis中令牌都过期,需要用户重新登录
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.getWriter().print("令牌已过期");
return false;
}
}catch (JWTDecodeException e){
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.getWriter().print("无效令牌");
return false;
}
//此时令牌没问题
boolean b = executeLogin(req, resp);
return b;
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse)servletResponse;
//允许跨域请求
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setContentType("text/html;charset=utf-8");
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
resp.getWriter().print(e.getMessage());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return false;
}
@Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
super.doFilterInternal(request, response, chain);
}
/**
* 获取token方法
*/
private String getRequestToken(HttpServletRequest req){
String token = req.getHeader("token");
if (StrUtil.isBlank(token)) {
//从请求体中获取数据
token=req.getParameter("token");
}
return token;
}
}