当前位置 主页 > 服务器问题 > Linux/apache问题 >

    Spring boot整合shiro+jwt实现前后端分离(3)

    栏目:Linux/apache问题 时间:2019-12-16 22:47

    到这里shiro配置就全部完成了

    下面开始配置jwt:

    首先我们需要重写  AuthenticationToken接口 此接口的作用

    负责把shiro中username,password生成用于验证的token的封装类

    所有我们需要去实现这个接口,封装我们自己生成的JWT生成的token

    import org.apache.shiro.authc.AuthenticationToken;
     
    /**
     * AuthenticationToken: shiro中负责把username,password生成用于验证的token的封装类
     * 我们需要自定义一个对象用来包装token。
     */
    public class JwtToken implements AuthenticationToken {
     
      private String token;
     
      public JwtToken(String token) {
        this.token = token;
      }
     
      public String getToken() {
        return token;
      }
     
      public void setToken(String token) {
        this.token = token;
      }
     
      @Override
      public Object getPrincipal() {
        return null;
      }
     
      @Override
      public Object getCredentials() {
        return null;
      }
    }

    因为我们是前后端分离项目所有我们不存在session验证之类的 所有每次请求都需要携带token,所以每次请求我们都需要去验证token的真实性,所有我们需要去实现  BasicHttpAuthenticationFilter过滤器  

    BasicHttpAuthenticationFilter继承 AuthenticatingFilter 过滤器其能够自动地进行基于所述传入请求的认证尝试。此实现是每个基本HTTP身份验证规范的Java实现  , 通过此过滤器得到HTTP请求资源获取Authorization传递过来的token参数   获取subject对象进行身份验证

    代码如下:

    import com.alibaba.fastjson.JSONObject;
    import com.serverprovider.config.shiro.jwt.JwtToken;
    import com.util.Util.utilTime;
    import org.apache.log4j.Logger;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    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.IOException;
    import java.io.PrintWriter;
    import java.util.concurrent.TimeUnit;
     
    /**
     *
     * BasicHttpAuthenticationFilter继承 AuthenticatingFilter 过滤器
     * 其能够自动地进行基于所述传入请求的认证尝试。
     *    BasicHttpAuthenticationFilter 基本访问认证过滤器
     *   此实现是每个基本HTTP身份验证规范的Java实现
     *   通过此过滤器得到HTTP请求资源获取Authorization传递过来的token参数
     *   获取subject对象进行身份验证
     *
     *
     */
    public class JwtFilter extends BasicHttpAuthenticationFilter {
     
      Logger logger = Logger.getLogger(JwtFilter.class);
     
      /**
       * 应用的HTTP方法列表配置基本身份验证筛选器。
       * 获取 request 请求 拒绝拦截登录请求
       * 执行登录认证方法
       *
       * @param request
       * @param response
       * @param mappedValue
       * @return
       */
      @Override
      protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        if (requestURI.equals("/user/login/verifyUser") || requestURI.equals("/user/register")) {
          return true;
        } else {
          try {
            executeLogin(request, response);
            return true;
          } catch (Exception e) {
            e.printStackTrace();
            return false;
          }
        }
      }
     
     
     
      /**
       * Authorization携带的参数为token
       * JwtToken实现了AuthenticationToken接口封装了token参数
       * 通过getSubject方法获取 subject对象
       * login()发送身份验证
       *
       * 为什么需要在Filter中调用login,不能在controller中调用login?
       * 由于Shiro默认的验证方式是基于session的,在基于token验证的方式中,不能依赖session做为登录的判断依据.
       * @param request
       * @param response
       */
      @Override
      protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        try{
          HttpServletRequest httpServletRequest = (HttpServletRequest) request;
          String token = httpServletRequest.getHeader("Authorization");
          JwtToken jwtToken = new JwtToken(token);
          // 提交给realm进行登入,如果错误他会抛出异常并被捕获
          Subject subject = getSubject(request, response);
          subject.login(jwtToken);
          logger.info("JWT验证用户信息成功");
          // 如果没有抛出异常则代表登入成功,返回true
          return true;
          }catch (Exception e){
          /* *
           * 这个问题纠结了好久
           *   原生的shiro验证失败会进入全局异常 但是 和JWT结合以后却不进入了 之前一直想不通
           *   原因是 JWT直接在过滤器里验证 验证成功与否 都是直接返回到过滤器中 成功在进入controller
           *   失败直接返回进入springboot自定义异常处理页面
           */
          JSONObject responseJSONObject = new JSONObject();
          responseJSONObject.put("result","401");
          responseJSONObject.put("resultCode","token无效,请重新获取。");
          responseJSONObject.put("resultData","null");
          responseJSONObject.put("resultTime", utilTime.StringDate());
          PrintWriter out = null;
          httpServletResponse.setCharacterEncoding("UTF-8");
          httpServletResponse.setContentType("application/json; charset=utf-8");
          logger.info("返回是");
          logger.info(responseJSONObject.toString());
          out = httpServletResponse.getWriter();
          out.append(responseJSONObject.toString());
        }
      return false;
      }
     
     
     
      /**
       * 对跨域提供支持
       */
      @Override
      protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
          httpServletResponse.setStatus(HttpStatus.OK.value());
          return false;
        }
        return super.preHandle(request, response);
      }
     
    }