查看完整视频
×

您需要在视频最下面评论,方可查看完整视频

您支付积分,方可查看完整视频

{{user.role.value}}

您支付费用,方可查看完整视频

¥{{user.role.value}}

只允许以下等级用户查看该视频

升级
会员专享
  • SpringSecurity基本原理

    今天我们主要介绍一下SpringSecurity框架的基本原理,因为我们在使用框架时不能只知其然,不知其所以然。掌握SpringSecurity框架的基本原理,有助于我们更好的掌握SpringSecurity框架的使用。

    实际上SpringSecurity框架的本质是通过拦截器链实现的。既然是拦截器链那就说明,一定是不指一个拦截器。下面我们简单介绍一下SpringSecurity框架中的主要的拦截器。

    • UsernamePasswordAuthenticationFilter   校验用户和密码登陆拦截器,该拦截器的作用是,当我们使用SpringSecurity框架的登陆功能是,就会自动触发此拦截器。下面我们简单看一下这个拦截器的核心的方法。
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }
    
            if (password == null) {
                password = "";
            }
    
            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    上面的代码比较简单此拦截器只是对账号和密码进行的验证,并没有做其它的逻辑处理。当验证通过时,就会请求到下一个拦截器。

    • BasicAuthenticationFilter   看名字我们知道这个是一个最基本的拦截器。当我们想使用这个拦截器时,需要在拦截器的配置里面指定httpBasic()方法。也就是如下面代码所示:
    @Configuration
     public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
         @Override
         protected void configure(HttpSecurity http) throws Exception {
             http.httpBasic()
                 .and()
                 .authorizeRequests()
                 .anyRequest()
                 .authenticated();
         }
     }

    这样当我们请求时就会默认先请求这个拦截器。除了httpBasic()方法外,我们还可以使用formLogin()方法,它俩的区别是一个对表单的验证,一个是针对HTTP头进行验证,通过开发时,可以用formLogin()方法用于登陆,用httpBasic()方法进行API验证。下面为此拦截器的核心代码:

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
       boolean debug = this.logger.isDebugEnabled();
       String header = request.getHeader("Authorization");
       if (header != null && header.toLowerCase().startsWith("basic ")) {
         try {
           String[] tokens = this.extractAndDecodeHeader(header, request);
           assert tokens.length == 2;
           String username = tokens[0];
           if (debug) {
             this.logger.debug("Basic Authentication Authorization header found for user '" + username + "'");
           }
           if (this.authenticationIsRequired(username)) {
             UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, tokens[1]);
             authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
             Authentication authResult = this.authenticationManager.authenticate(authRequest);
             if (debug) {
               this.logger.debug("Authentication success: " + authResult);
             }
             SecurityContextHolder.getContext().setAuthentication(authResult);
             this.rememberMeServices.loginSuccess(request, response, authResult);
             this.onSuccessfulAuthentication(request, response, authResult);
           }
         } catch (AuthenticationException var10) {
           SecurityContextHolder.clearContext();
           if (debug) {
             this.logger.debug("Authentication request for failed: " + var10);
           }
           this.rememberMeServices.loginFail(request, response);
           this.onUnsuccessfulAuthentication(request, response, var10);
           if (this.ignoreFailure) {
             chain.doFilter(request, response);
           } else {
             this.authenticationEntryPoint.commence(request, response, var10);
           }
           return;
         }
         chain.doFilter(request, response);
       } else {
         chain.doFilter(request, response);
       }
     }
    • FilterSecurityInterceptor   最牛逼的拦截器,因为所有的拦截器最后,都要通过它的校验,此拦截器的作用是控制能不能继续向下请求。如果能请求,则放行可以继续访问我们的接口,如不能,则抛出异常。下面为此拦截器的核心代码:
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
      if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
      } else {
        if (fi.getRequest() != null && this.observeOncePerRequest) {
          fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
        }
    
        InterceptorStatusToken token = super.beforeInvocation(fi);
    
        try {
          fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
          super.finallyInvocation(token);
        }
    
        super.afterInvocation(token, (Object)null);
      }
    
    }
    • ExceptionTranslationFilter   异常拦截器。当我们通过上述拦截器验证失败时,就会抛出相应的异常。这时此拦截器,就开始发挥它的作用了。它会捕获相应的异常,来决定,是继续请求下一下拦截器,还是跳转登陆,还是直接抛出相应的错误信息。下面为此拦截器的核心代码。
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
       HttpServletRequest request = (HttpServletRequest)req;
       HttpServletResponse response = (HttpServletResponse)res;
       try {
         chain.doFilter(request, response);
         this.logger.debug("Chain processed normally");
       } catch (IOException var9) {
         throw var9;
       } catch (Exception var10) {
         Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
         RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
         if (ase == null) {
           ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
         }
         if (ase == null) {
           if (var10 instanceof ServletException) {
             throw (ServletException)var10;
           }
           if (var10 instanceof RuntimeException) {
             throw (RuntimeException)var10;
           }
           throw new RuntimeException(var10);
         }
         if (response.isCommitted()) {
           throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var10);
         }
         this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
       }
     }

    看上述的代码,我们发现此拦截器的核心功能,均写在了catch方法里,这样其它拦截器抛出异常时,此拦截器就会成功的捕获到。

    下面我们介绍一下上面4个拦截在使用时的注意事项。UsernamePasswordAuthenticationFilter和BasicAuthenticationFilter拦截器我们可通过在配置文件中设置来决定此拦截器是否生效。而ExceptionTranslationFilter和FilterSecurityInterceptor拦截器则不可以通过配置文件设置,这两个拦截器一直会在拦截器链中发挥作用。当然我们也可以指定我们自己的拦截器。具体情况也就是下图中所示的那样,绿色部分的拦截器,我们均可以通过参数配置是否有效,而蓝色和橙色的拦截器我们则不可以让它们失效。

    title
    0 条回复 A文章作者 M管理员
      暂无讨论,说说你的看法吧