package com.yuanchu.limslaboratory.shiro.filter;
|
|
import com.yuanchu.limslaboratory.shiro.JwtToken;
|
import com.yuanchu.limslaboratory.shiro.utils.JwtUtils;
|
import org.apache.shiro.authc.AuthenticationException;
|
import org.apache.shiro.authc.AuthenticationToken;
|
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
import org.apache.shiro.web.util.WebUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.http.HttpStatus;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.ObjectUtils;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
|
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletResponse;
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.PrintWriter;
|
|
@Component
|
public class JwtFilter extends BasicHttpAuthenticationFilter {
|
private Logger log = LoggerFactory.getLogger(this.getClass());
|
|
public Integer code;
|
public String message;
|
|
/**
|
* 前置处理
|
*/
|
@Override
|
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
|
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
|
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
|
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
|
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
|
httpServletResponse.setStatus(HttpStatus.OK.value());
|
return false;
|
}
|
return super.preHandle(request, response);
|
}
|
|
/**
|
* 后置处理
|
*/
|
@Override
|
protected void postHandle(ServletRequest request, ServletResponse response) {
|
// 添加跨域支持
|
this.fillCorsHeader(WebUtils.toHttp(request), WebUtils.toHttp(response));
|
}
|
|
/**
|
* 过滤器拦截请求的入口方法
|
* 返回 true 则允许访问
|
* 返回false 则禁止访问,会进入 onAccessDenied()
|
*/
|
@Override
|
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
// 如果token为空不让通过
|
String header = ((HttpServletRequest) request).getHeader(JwtUtils.AUTH_HEADER);
|
if (ObjectUtils.isEmpty(header)){
|
code = 503;
|
message = "STS token解码过程中,由于内部调用失败导致解码失败,请稍后再试。";
|
return false;
|
}
|
boolean allowed = false;
|
try {
|
// 检测Header里的 JWT token内容是否正确,尝试使用 token进行登录
|
allowed = executeLogin(request, response);
|
} catch (IllegalStateException e) { // not found any token
|
log.error("Not found any token");
|
} catch (Exception e) {
|
log.error("Error occurs when login", e);
|
}
|
return allowed || super.isPermissive(mappedValue);
|
}
|
|
|
/**
|
* 身份验证,检查 JWT token 是否合法
|
*/
|
@Override
|
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
|
AuthenticationToken token = createToken(request, response);
|
if (token == null) {
|
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
|
+ "must be created in order to execute a login attempt.";
|
throw new IllegalStateException(msg);
|
}
|
|
try {
|
Subject subject = getSubject(request, response);
|
subject.login(token); // 交给 Shiro 去进行登录验证
|
return onLoginSuccess(token, subject, request, response);
|
} catch (AuthenticationException e) {
|
return onLoginFailure(token, e, request, response);
|
}
|
}
|
|
/**
|
* 从 Header 里提取 JWT token
|
*/
|
@Override
|
protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
|
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
|
String authorization = httpServletRequest.getHeader(JwtUtils.AUTH_HEADER);
|
JwtToken token = new JwtToken(authorization);
|
return token;
|
}
|
|
/**
|
* isAccessAllowed()方法返回false,会进入该方法,表示拒绝访问
|
*/
|
@Override
|
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
|
HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse);
|
httpResponse.setCharacterEncoding("UTF-8");
|
httpResponse.setContentType("application/json;charset=UTF-8");
|
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
|
PrintWriter writer = httpResponse.getWriter();
|
writer.write("{\"errCode\": "+ code +", \"msg\": \"" + message +"\"}");
|
fillCorsHeader(WebUtils.toHttp(servletRequest), httpResponse);
|
return false;
|
}
|
|
/**
|
* Shiro 利用 JWT token 登录成功,会进入该方法
|
*/
|
@Override
|
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
|
ServletResponse response) {
|
HttpServletResponse httpResponse = WebUtils.toHttp(response);
|
String newToken = null;
|
if (token instanceof JwtToken) {
|
newToken = JwtUtils.refreshTokenExpired(token.getCredentials().toString(), JwtUtils.SECRET);
|
}
|
if (newToken != null)
|
httpResponse.setHeader(JwtUtils.AUTH_HEADER, newToken);
|
return true;
|
}
|
|
/**
|
* Shiro 利用 JWT token 登录失败,会进入该方法
|
*/
|
@Override
|
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
|
ServletResponse response) {
|
// 此处直接返回 false ,交给后面的 onAccessDenied()方法进行处理
|
String error = e.getClass().toString().split("\\.")[4];
|
if (error.equals("IncorrectCredentialsException")) {
|
code = 10010;
|
} else {
|
code = 401;
|
}
|
message = e.getMessage();
|
return false;
|
}
|
|
/**
|
* 添加跨域支持
|
*/
|
protected void fillCorsHeader(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
|
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
|
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,HEAD");
|
httpServletResponse.setHeader("Access-Control-Allow-Headers",
|
httpServletRequest.getHeader("Access-Control-Request-Headers"));
|
}
|
}
|