zoukankan      html  css  js  c++  java
  • SpringSecurity学习:简介、如何集成、拦截规则、忽略规则、自定义页面、参数详解、校验流程

      SpringSecurity是一个安全框架,主要用于授权和认证,在普通项目中,我们使用过滤器和拦截器也可以实现,但是使用SpringSecurity更加简单。

    一、spring security 简介

            spring security 的核心功能主要包括:

    • 认证 (你是谁)
    • 授权 (你能干什么)
    • 攻击防护 (防止伪造身份)

      其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份。在spring security中一种过滤器处理一种认证方式。

      比如,对于username password认证过滤器来说,会检查是否是一个登录请求;是否包含username 和 password (也就是该过滤器需要的一些认证信息),如果不满足则放行给下一个。

      下一个按照自身职责判定是否是自身需要的信息,basic的特征就是在请求头中有 Authorization:Basic eHh4Onh4 的信息。

      中间可能还有更多的认证过滤器。最后一环是 FilterSecurityInterceptor,这里会判定该请求是否能进行访问rest服务,判断的依据是 BrowserSecurityConfig 中的配置,如果被拒绝了就会抛出不同的异常(根据具体的原因)。

      Exception Translation Filter 会捕获抛出的错误,然后根据不同的认证方式进行信息的返回提示。

      注意:绿色的过滤器可以配置是否生效,其他的都不能控制。

    二、整合SpringSecurity

    1、在pom文件中导入依赖

    <dependencies>
        ...
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
        ...
    </dependencies>

    2、新建配置类

    package com.opengauss.exam.security;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
      // 授权 @Override
    protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/test").permitAll() .anyRequest().authenticated().and() .formLogin(); }   // 认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("gwf").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"); } }

      该配置类的意思是:直接访问 /test 可以访问,但是若访问 /hello 则需要先登录。

      WebSecurityConfig类使用了@EnableWebSecurity注解 ,以启用SpringSecurity的Web安全支持,并提供Spring MVC集成。它还扩展了WebSecurityConfigurerAdapter,并覆盖了一些方法来设置Web安全配置的一些细节。

      configure(HttpSecurity)方法定义了哪些URL路径应该被保护,哪些不应该。具体来说,“/test”路径被配置为不需要任何身份验证,所有其他路径必须经过身份验证。

      当用户成功登录时,它们将被重定向到先前请求的需要身份认证的页面。有一个由 loginPage()指定的自定义“/登录”页面,每个人都可以查看它。

      对于configure(AuthenticationManagerBuilder) 方法,它将单个用户设置在内存中。该用户的用户名为“gwf”,密码为“123456”,角色为“vip1”。

    3、下面我们看下其他详细拦截规则:

    // 案例1
    http.authorizeRequests()
        //请求路径“/test”容许访问
        .antMatchers("/test").permitAll()
      //其它请求都需要校验才能访问
      .anyRequest().authenticated()
      .and()
      // 定义登录的页面为“/login”,容许访问
      .formLogin().loginPage("/login").permitAll()
      .and()
      //默认的“/logout”,容许访问
      .logout().permitAll();
    
    // 案例2
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests()
        .antMatchers("/").permitAll()
        //必须有“USER”角色的才能访问
        .antMatchers("/user/**").hasAuthority("USER")
        .and()
        //登陆成功以后默认访问路径/user
        .formLogin().loginPage("/login").defaultSuccessUrl("/user")
        .and()
        //注销以后默认访问路径/login
        .logout().logoutUrl("/logout").logoutSuccessUrl("/login");
    
      http.addFilterAt(customFromLoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    
    // 案例3
    @Override
    public void configure(WebSecurity web) throws Exception {
      // 设置下面为忽略地址   web.ignoring().antMatchers(
    "/js/**","/css/**","/img/**","/webjars/**"); }

    4、自定义页面

      前面我们的登录页面都是使用的SpringSecurity默认的,我们可以在配置类中修改成我们自定义的登录页面

    (1)自定义登录页面:resources/templates/login.html

      SpringSecurity的name属性默认是usernamepassword,这里我们采用自定义的方式,改成:user 与 pwd,/login是SpringSecurity默认的处理登录的Controller

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>login</title>
    </head>
    <body>
    <form action="/login" method="post">
        用户名:<input type="text" name="user"><br>
        密码:<input type="password" name="pwd"><br>
        <input type="radio" name="remember">记住我
        <button type="submit">提交</button>
    </form>
    </body>
    </html>

    (2)修改SecurityConfig配置类

      在configure(HttpSecurity http)方法中添加如下内容,(注意这里我们要禁止csrf,否则登录会被拦截):

    // 开启登录页面,即没有权限的话跳转到登录页面,对应地址:/login
    http.formLogin()
            // 登录页面
            .loginPage("/toLogin")
            // 用户名的name
            .usernameParameter("user")
            // 密码的name
            .passwordParameter("pwd")
            // 处理登录的Controller
            .loginProcessingUrl("/login");
    http.csrf().disable();
    // 开启记住我功能,默认保存两周
    http.rememberMe()
            // name属性
            .rememberMeParameter("remember");

    (3)编写Controller

    @GetMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

      此时,我们就可以使用自定义登录页面了。

    三、参数详解

    1、注解 @EnableWebSecurity

      在 SpringBoot 应用中使用 Spring Security,用到了 @EnableWebSecurity 注解,官方说明为,该注解和 @Configuration 注解一起使用,注解 WebSecurityConfigurer 类型的类,或者利用@EnableWebSecurity 注解继承 WebSecurityConfigurerAdapter的类,这样就构成了 Spring Security 的配置。

    2、抽象类 WebSecurityConfigurerAdapter

      一般情况,会选择继承 WebSecurityConfigurerAdapter 类,其官方说明为:WebSecurityConfigurerAdapter 提供了一种便利的方式去创建 WebSecurityConfigurer的实例,只需要重写 WebSecurityConfigurerAdapter 的方法,即可配置拦截什么URL、设置什么权限等安全控制。

    3、方法 configure(AuthenticationManagerBuilder auth) 和 configure(HttpSecurity http)

         Demo 中重写了 WebSecurityConfigurerAdapter 的两个方法:

    /**
         * 通过 {@link #authenticationManager()} 方法的默认实现尝试获取一个 {@link AuthenticationManager}.
         * 如果被复写, 应该使用{@link AuthenticationManagerBuilder} 来指定 {@link AuthenticationManager}.
         *
         * 例如, 可以使用以下配置在内存中进行注册公开内存的身份验证{@link UserDetailsService}:
         *
         * // 在内存中添加 user 和 admin 用户
         * @Override
         * protected void configure(AuthenticationManagerBuilder auth) {
         *     auth
         *       .inMemoryAuthentication().withUser("user").password("password").roles("USER").and()
         *         .withUser("admin").password("password").roles("USER", "ADMIN");
         * }
         *
         * // 将 UserDetailsService 显示为 Bean
         * @Bean
         * @Override
         * public UserDetailsService userDetailsServiceBean() throws Exception {
         *     return super.userDetailsServiceBean();
         * }
         *
         */
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            this.disableLocalConfigureAuthenticationBldr = true;
        }
     
     
        /**
         * 复写这个方法来配置 {@link HttpSecurity}. 
         * 通常,子类不能通过调用 super 来调用此方法,因为它可能会覆盖其配置。 默认配置为:
         * 
         * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
         *
         */
        protected void configure(HttpSecurity http) throws Exception {
            logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
    ​
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin().and()
                .httpBasic();
        }

    4、final 类 HttpSecurity

      HttpSecurity 常用方法及说明:

    openidLogin()    // 用于基于 OpenId 的验证
    headers()    // 将安全标头添加到响应
    cors()    // 配置跨域资源共享( CORS )
    sessionManagement()    // 允许配置会话管理
    portMapper()    // 允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),
      其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,
      Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
    jee() // 配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理 x509() // 配置基于x509的认证 rememberMe // 允许配置“记住我”的验证 authorizeRequests() // 允许基于使用HttpServletRequest限制访问 requestCache() // 允许配置请求缓存 exceptionHandling() // 允许配置错误处理 securityContext() // 在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。
      当使用WebSecurityConfigurerAdapter时,这将自动应用
    servletApi() // 将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。
      当使用WebSecurityConfigurerAdapter时,这将自动应用
    csrf() // 添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用 logout() // 添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。
      默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,
      清除SecurityContextHolder,然后重定向到”/login?success”
    anonymous() // 允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。
      默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,
      并包含角色 “ROLE_ANONYMOUS”
    formLogin() // 指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面 oauth2Login() // 根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证 requiresChannel() // 配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射 httpBasic() // 配置 Http Basic 验证 addFilterAt() // 在指定的Filter类的位置添加过滤器

    5、类 AuthenticationManagerBuilder

    /**
    * {@link SecurityBuilder} used to create an {@link AuthenticationManager}. Allows for
    * easily building in memory authentication, LDAP authentication, JDBC based
    * authentication, adding {@link UserDetailsService}, and adding
    * {@link AuthenticationProvider}'s.
    */

      意思是,AuthenticationManagerBuilder 用于创建一个 AuthenticationManager,让我能够轻松的实现内存验证、LADP验证、基于JDBC的验证、添加UserDetailsService、添加AuthenticationProvider。

    四、校验流程图

    五、源码解析

      上面的流程图就是spring security的整体验证流程!

    1、首先所有的请求都会走AbstractAuthenticationProcessingFilter.doFilter(req, res, chain)方法

    2、判断请求是否需要验证

    3、authResult = attemptAuthentication(request, response) 进行用户验证(request中会带有用户的信息),该方法也是验证过程中最重要的方法

      (1)返回一个 Authentication 对象,说明验证成功

      (2)验证时发生 AuthenticationException。

      (3)返回Null,表示身份验证不完整。假设子类做了一些必要的工作(如重定向)来继续处理验证,方法将立即返回。假设后一个请求将被这种方法接收,其中返回的Authentication对象不为空。

      接下来我们来看一下AbstractAuthenticationProcessingFilter到底是怎么搞得?

      首先AbstractAuthenticationProcessingFilter抽象类,咱们看一下继承它的子类

      从上图我们可以看出UsernamePasswordAuthenticationFilter类是它的子类,那么我们看一下UsernamePasswordAuthenticationFilter类attemptAuthentication(request, response)方法是怎么搞得

      从代码可以看出:首先用用户名和密码生成个token,然后去验证token;

      那么问题来了,是谁去验证的token?别急我们继续跟代码。通过跟代码我们可以得出,原来AbstractAuthenticationProcessingFilter类中有一个private AuthenticationManager authenticationManager成员变量,也就是通过它去验证token;

      下面我们看一下AuthenticationManager 类:

      通过查看原来AuthenticationManager是一个验证管理器接口,既然是接口那一定有实现它的实现类!我们继续跟!!!

      通过查看代码,就ProviderManager类像正常点的,那我们继续看看ProviderManager是到底在搞什么

      以上就是ProviderManager的authenticate(Authentication authentication),它是实现AuthenticationManager接口的,主要看一下红线指向两处:

    (1)有个这个东西【getProviders()】,然后遍历它,

    (2)AuthenticationProvider对象去进行验证token!!【result = provider.authenticate(authentication)】;

      通过查看代码原来ProviderManager类里面有这个属性private List providers = Collections.emptyList();  也就是getProviders();

      既然干活的是AuthenticationProvider对象,那就再看看它是怎么搞得!

      AuthenticationProvider接口:

      AbstractUserDetailsAuthenticationProvider通过名字,你有没有什么想法?抽象的用户详情验证提供者!!!那么我们看一下authenticate(Authentication authentication)方法!

      retrieveUser方法

      查看源码,他是一个抽象的方法;那接下来我们看一下它的实现类!DaoAuthenticationProvider

      看箭头,有没有什么茅塞顿开!!!this.getUserDetailsService().loadUserByUsername(username);就是我们自己实现的UserDetailsService接口的实现类,我们通过实现loadUserByUsername(username)方法,获取userDetail对象;然后通过additionalAuthenticationChecks方法检验!!!

      看到这里,如果之前没有用过spring security 的人一定会一头雾水~没事儿,多看看就好了,再结合网上的Demo自己感觉感觉!!!

      总结:其实看着有点云里雾里~但是中心思想,就是把验证用户信息的一整套流程预先已经定义好了,封装在一个方法中(模板模式),然后各种暴露接口,抽象类,让子类去实现具体的业务逻辑

      源码部分文章:https://www.jianshu.com/p/dd42cb2b46dc

  • 相关阅读:
    64位windows 7下配置TortoiseGit(转)
    linux中fork函数详解(转)
    Socket通信的Python实现
    Socket
    浅谈CSRF攻击方式(转)
    Burpsuite常用模块详解以及渗透测试上的运用
    大佬内网渗透技巧的独白(思路篇)
    CTFcrackTools-V3
    厂商要知道的漏洞防护措施
    如何运用kali-xplico网络取证分析?点开看看吧
  • 原文地址:https://www.cnblogs.com/goloving/p/14880569.html
Copyright © 2011-2022 走看看