123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- package com.yaozhitech.spring5.filter;
- import java.time.LocalDateTime;
- import java.time.ZoneId;
- import java.util.Date;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- 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.AuthenticatingFilter;
- import org.apache.shiro.web.util.WebUtils;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.util.StringUtils;
- import org.springframework.web.bind.annotation.RequestMethod;
- import com.yaozhitech.spring5.dto.UserDto;
- import com.yaozhitech.spring5.jwt.JWTToken;
- import com.yaozhitech.spring5.service.UserService;
- import com.yaozhitech.spring5.utils.JwtUtils;
- import lombok.extern.slf4j.Slf4j;
- @Slf4j
- public class JwtAuthFilter extends AuthenticatingFilter {
-
- @Value("${jwt.header}")
- private String tokenHeader;
- @Value("${jwt.tokenHead}")
- private String tokenHead;
- private static final int tokenRefreshInterval = 300;
- private UserService userService;
- public JwtAuthFilter(UserService userService){
- this.userService = userService;
- this.setLoginUrl("/login");
- }
- @Override
- protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
- HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
- if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) //对于OPTION请求做拦截,不做token校验
- return false;
- return super.preHandle(request, response);
- }
- @Override
- protected void postHandle(ServletRequest request, ServletResponse response){
- this.fillCorsHeader(WebUtils.toHttp(request), WebUtils.toHttp(response));
- request.setAttribute("jwtShiroFilter.FILTERED", true);
- }
- /**
- * 父类会在请求进入拦截器后调用该方法,返回true则继续,返回false则会调用onAccessDenied()。这里在不通过时,还调用了isPermissive()方法,
- */
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- if(this.isLoginRequest(request, response))
- return true;
- Boolean afterFiltered = (Boolean)(request.getAttribute("jwtShiroFilter.FILTERED"));
- if(afterFiltered != null && afterFiltered)
- return true;
-
- boolean allowed = false;
- try {
- allowed = executeLogin(request, response);
- } catch(IllegalStateException e){ //not found any token
- log.error("Not found any token", e);
- }catch (Exception e) {
- log.error("Error occurs when login", e);
- }
- return allowed;// || super.isPermissive(mappedValue);
- }
- /**
- * 这里重写了父类的方法,使用我们自己定义的Token类,提交给shiro。这个方法返回null的话会直接抛出异常,进入isAccessAllowed()的异常处理逻辑。
- */
- @Override
- protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
- String jwtToken = getAuthzHeader(servletRequest);
- if(!StringUtils.isEmpty(jwtToken)&&!JwtUtils.isTokenExpired(jwtToken))
- return new JWTToken(jwtToken);
- return null;
- }
- @Override
- protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
-
- HttpServletRequest httpServletRequest = WebUtils.toHttp(servletRequest);
- HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse);
- // 返回401
- httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- httpResponse.getOutputStream().println("401 UNAUTHORIZED");
- // 设置响应码为401或者直接输出消息
- String url = httpServletRequest.getRequestURI();
- log.error("onAccessDenied url:{}", url);
- return false;
- }
- /**
- * 如果Shiro Login认证成功,会进入该方法,等同于用户名密码登录成功,我们这里还判断了是否要刷新Token
- */
- @Override
- protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
- HttpServletResponse httpResponse = WebUtils.toHttp(response);
- String newToken = null;
- if(token instanceof JWTToken){
- JWTToken jwtToken = (JWTToken)token;
- UserDto user = (UserDto) subject.getPrincipal();
- boolean shouldRefresh = shouldTokenRefresh(JwtUtils.getIssuedAt(jwtToken.getToken()));
- if(shouldRefresh) {
- newToken = userService.generateJwtToken(user.getUsername());
- }
- }
- if(!StringUtils.isEmpty(newToken))
- httpResponse.setHeader("x-auth-token", newToken);
- return true;
- }
- @Override
- protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
- log.error("Validate token fail, token:{}, error:{}", token.toString(), e.getMessage());
- return false;
- }
- protected String getAuthzHeader(ServletRequest request) {
- HttpServletRequest httpRequest = WebUtils.toHttp(request);
- String header = httpRequest.getHeader("x-auth-token");
- if (StringUtils.startsWithIgnoreCase(header, "Bearer ")) {
- return StringUtils.replace(header, "Bearer ", "");
- }
- return header;
- }
- protected boolean shouldTokenRefresh(Date issueAt){
- LocalDateTime issueTime = LocalDateTime.ofInstant(issueAt.toInstant(), ZoneId.systemDefault());
- return LocalDateTime.now().minusSeconds(tokenRefreshInterval).isAfter(issueTime);
- }
- 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"));
- }
- }
|