Spring Security-配置


完成了基本配置后,我们需要自定义Spring Security的一些进阶配置,才能应用于我们的项目。

和SpringBoot项目中其他插件一样,我们一般也是写一个配置类来存放对Spring Security的配置。

@EnableWebSecurity

我们自己定义的配置类 WebSecurityConfig 加上了 @EnableWebSecurity 注解,同时继承了 WebSecurityConfigurerAdapter。你可能会在想谁的作用大一点,毫无疑问 @EnableWebSecurity 起到决定性的配置作用,它其实是个组合注解。

@Import({ WebSecurityConfiguration.class, // <2>
      SpringWebMvcImportSelector.class }) // <1>
@EnableGlobalAuthentication // <3>
@Configuration
public @interface EnableWebSecurity {
   boolean debug() default false;
}

@Import 是 springboot 提供的用于引入外部的配置的注解,可以理解为:@EnableWebSecurity 注解激活了 @Import 注解中包含的配置类。

<1> SpringWebMvcImportSelector 的作用是判断当前的环境是否包含 springmvc,因为 spring security 可以在非 spring 环境下使用,为了避免 DispatcherServlet 的重复配置,所以使用了这个注解来区分。

<2> WebSecurityConfiguration 顾名思义,是用来配置 web 安全的,下面的小节会详细介绍。

<3> @EnableGlobalAuthentication 注解的源码如下:

@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

注意点同样在 @Import 之中,它实际上激活了 AuthenticationConfiguration 这样的一个配置类,用来配置认证相关的核心类。

也就是说:@EnableWebSecurity 完成的工作便是加载了 WebSecurityConfigurationAuthenticationConfiguration 这两个核心配置类,也就此将 spring security 的职责划分为了配置安全信息,配置认证信息两部分。

WebSecurityConfigurerAdapter

适配器模式在 spring 中被广泛的使用,在配置中使用 Adapter 的好处便是,我们可以选择性的配置想要修改的那一部分配置,而不用覆盖其他不相关的配置。WebSecurityConfigurerAdapter 中我们可以选择自己想要修改的内容,来进行重写,而其提供了三个 configure 重载方法,是我们主要关心的:

由参数就可以知道,分别是对 AuthenticationManagerBuilder,WebSecurity,HttpSecurity 进行个性化的配置。

配置SecurityConfig

在项目源代码目录下建立config包,创建 SecurityConfig 配置类,继承 WebSecurityConfigurerAdapter 抽象类,实现 Spring Security 在 Web 场景下的自定义配置。

重写configure(AuthenticationManagerBuilder auth)

想要在 WebSecurityConfigurerAdapter 中进行认证相关的配置,可以使用 configure(AuthenticationManagerBuilder auth) 暴露一个 AuthenticationManager 的建造器:AuthenticationManagerBuilder 如下所示:

// SecurityConfig.java

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.
            // <X> 使用内存中的 InMemoryUserDetailsManager
            inMemoryAuthentication()
            // <Y> 不使用 PasswordEncoder 密码编码器
            .passwordEncoder(NoOpPasswordEncoder.getInstance())
            // <Z> 配置 admin 用户
            .withUser("admin").password("admin").roles("ADMIN")
            // <Z> 配置 normal 用户
            .and().withUser("normal").password("normal").roles("NORMAL");
}

说明

处,调用 AuthenticationManagerBuilder#inMemoryAuthentication() 方法,使用内存级别的 InMemoryUserDetailsManager Bean 对象,提供认证的用户信息。

Spring 内置了两种 UserDetailsManager 实现:

  1. InMemoryUserDetailsManager
  2. JdbcUserDetailsManager ,基于 JDBC的 JdbcUserDetailsManager 。

实际项目中,我们更多采用调用AuthenticationManagerBuilder#userDetailsService(userDetailsService) 方法,使用自定义实现的 UserDetailsService 实现类,更加灵活、自由地实现认证的用户信息的读取。

处,调用 AbstractDaoAuthenticationConfigurer#passwordEncoder(passwordEncoder) 方法,设置 PasswordEncoder 密码编码器。在这里,为了方便,我们使用 NoOpPasswordEncoder 。实际上,等于不使用 PasswordEncoder 。 NoOpPasswordEncoderencode方法就只是简单地把字符序列转成字符串,也就是说,你输入的密码”123456”存储在数据库里仍然是”123456”,这样如果数据库被攻破的话,密码就直接泄露了,十分不安全。

不过,正因其十分简单,所以在Spring Security 5.0 之前NoOpPasswordEncoder是作为默认的密码编码器而存在的,它可以是你没有主动加密时的一个默认选择。

生产环境下,推荐使用 BCryptPasswordEncoder 。更多关于 PasswordEncoder 的内容,推荐阅读《该如何设计你的 PasswordEncoder?》文章。

处,配置了「admin/admin」和「normal/normal」两个用户,分别对应 ADMIN 和 NORMAL 角色。还可以配置更多的用户。

重写configure(HttpSecurity http)

然后,我们重写 configure(HttpSecurity http) 方法,主要配置 URL 的权限控制。

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("test/demo").permitAll()
                // 配置请求地址的权限
                .antMatchers("test/admin").hasRole("ADMIN")
                .antMatchers("test/normal").hasRole("NORMAL")
                .anyRequest().authenticated()
                // 设置 Form 表单登陆
                .and().formLogin()
            	//loginPage("/login")
            	.permitAll()
                .and().logout()
            	//logoutUrl("/logout")
            	.permitAll();
    }

上述是一个使用 Java Configuration 配置 HttpSecurity 的典型配置,其中 http 作为根开始配置,每一个 and()对应了一个模块的配置(等同于 xml 配置中的结束标签),并且 and() 返回了 HttpSecurity 本身,于是可以连续进行配置。(链式编程调用)

他们配置的含义也非常容易通过变量本身来推测,

  • authorizeRequests() 配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
  • formLogin() 对应表单认证相关的配置
  • logout() 对应了注销相关的配置
  • httpBasic() 可以配置 basic 登录
  • etc

他们分别代表了 http 请求相关的安全配置,这些配置项无一例外的返回了 Configurer 类,而所有的 http 相关配置可以通过查看 HttpSecurity 的主要方法得知:

这里要特别注意:如果配置了loginPage和logoutUrl这两项,就必须提供html文件来构成 登陆页面,否则就会无法登录。所以这里不配置这两项,还是使用系统默认给的登陆页面。

需要对 http 协议有一定的了解才能完全掌握所有的配置,不过,springboot 和 spring security 的自动配置已经足够使用了。其中每一项 Configurer(e.g.FormLoginConfigurer,CsrfConfigurer)都是 HttpConfigurer 的细化配置项。

调用 HttpSecurity.authorizeRequests() 方法,开始配置 URL 的权限控制

下面,是配置权限控制会使用到的方法:

  • #String... antPatterns) 方法,配置匹配的 URL 地址,基于 Ant 风格路径表达式 ,可传入多个。
  • 【常用】permitAll() 方法,所有用户可访问。
  • 【常用】denyAll() 方法,所有用户不可访问。
  • 【常用】authenticated() 方法,登录用户可访问。
  • anonymous() 方法,无需登录,即匿名用户可访问。
  • rememberMe() 方法,通过 remember me 登录的用户可访问。
  • fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
  • hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
  • 【常用】hasRole(String role) 方法, 拥有指定角色的用户可访问。
  • 【常用】hasAnyRole(String... roles) 方法,拥有指定任一角色的用户可访问。
  • 【常用】hasAuthority(String authority) 方法,拥有指定权限(authority)的用户可访问。
  • 【常用】hasAuthority(String... authorities) 方法,拥有指定任一权限(authority)的用户可访问。
  • 【最牛】access(String attribute) 方法,当 Spring EL 表达式的执行结果为 true 时,可以访问。

@PermitAll 注解,等价于 permitAll() 方法,所有用户可访问。

重要!!!因为在SecurityConfig中,配置了 .anyRequest().authenticated() ,任何请求,访问的用户都需要经过认证。所以这里 @PermitAll 注解实际是不生效的

也就是说,Java Config 配置的权限,和注解配置的权限,两者是叠加的。

@PreAuthorize 注解,等价于 access(String attribute) 方法,,当 Spring EL 表达式的执行结果为 true 时,可以访问。

重写configure(WebSecurity web)

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
            .ignoring()
            .antMatchers("/resources/**");
    }
}

这个方法看起来不是太常用……


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM