Spring Security介绍

1 项目模块

1.1 核心模块 — spring-security-core.jar

包含核心的验证和访问控制类和接口,远程支持和基本的配置API. 任何使用Spring Security的应用程序都需要这个模块. 支持独立应用程序、远程客户端、服务层方法安全和JDBC用户配置. 包含以下顶层包:

  • org.springframework.security.core

  • org.springframework.security.access

  • org.springframework.security.authentication

  • org.springframework.security.provisioning

1.2 Web — spring-security-web.jar

该模块包含过滤器和相关的 Web 安全基础结构代码. 它包含任何与 Servlet API 相关的内容. 如果需要 Spring Security Web 认证服务和基于URL的访问控制,则需要它. 主要包是 org.springframework.security.web.

1.3 配置 — spring-security-config.jar

包含安全命名空间解析和 Java 配置代码. 如果您使用 Spring Security XML 命名空间进行配置或 Spring Security 的Java配置支持,则需要它. 主包名为 org.springframework.security.config.这些类都不打算直接在应用程序中使用.
HttpSecurity(HTTP请求安全处理),AuthenticationManagerBuilder(身份验证管理生成器)和WebSecurity(WEB安全)

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

       @Override
       protected void configure(AuthenticationManagerBuilder auth){
            super.configure(auth);
       }

       @Override
       protected void configure(HttpSecurity http){
            super.configure(http);
       }

       @Override
       protected void configure(WebSecurity web){
            super.configure(web);
       }

1.4.OAuth 2.0 核心包 — spring-security-oauth2-core.jar

包含核心类和接口,这些类和接口提供对 OAuth 2.0授权框架和 OpenID Connect Core 1.0的支持. 使用 OAuth 2.0 或 OpenID Connect Core 1.0的应用程序 (例如客户端,资源服务器和授权服务器) 需要它. 顶层的包是 org.springframework.security.oauth2.core.

1.5 OAuth 2.0 客户端 — spring-security-oauth2-client.jar

spring-security-oauth2-client.jar 包含 Spring Security 对 OAuth 2.0 授权框架和 OpenID Connect Core 1.0的客户端支持. 使用 OAuth 2.0登录 或 OAuth客户端支持的应用程序需要使用它. 顶层的包是 org.springframework.security.oauth2.client.

1.6 OAuth 2.0 资源 服务器 — spring-security-oauth2-resource-server.jar

spring-security-oauth2-resource-server.jar 包含 Spring Security对OAuth 2.0资源服务器的支持. 它用于通过 OAuth 2.0 Bearer 令牌保护API. 顶层的包是 org.springframework.security.oauth2.server.resource.

spring security模块.png

记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

认证

UserDetailsService

DaoAuthenticationProvider 使用 UserDetailsService 检索用户名,密码和其他用于使用用户名和密码进行身份验证的属性. Spring Security 提供 UserDetailsService 的 内存中 和 JDBC 实现.
可以通过将自定义 UserDetailsService 暴露为bean来定义自定义身份验证.

PasswordEncoder

Spring Security 的 servlet 支持与 PasswordEncoder 集成来安全地存储密码. 可以通过 暴露一个 PasswordEncoder Bean 来定制 Spring Security 使用的 PasswordEncoder 实现.

授权

protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeRequests(authorize -> authorize                                  
            .mvcMatchers("/resources/**", "/signup", "/about").permitAll()         
            .mvcMatchers("/admin/**").hasRole("ADMIN")                             
            .mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")   
            .anyRequest().denyAll()                                                
        );
}
指定了多个授权规则.  每个规则均按其声明顺序进行考虑.
我们指定了任何用户都可以访问的多个URL模式.  具体来说,如果URL以 "/resources/" 开头,等于 "/signup" 或等于 "/about",则任何用户都可以访问请求.
以 "/admin/" 开头的任何URL都将限于角色为 ROLE_ADMIN 的用户.  您将注意到,由于我们正在调用 hasRole 方法,因此无需指定 ROLE_ 前缀.
任何以 "/db/" 开头的URL都要求用户同时具有 "ROLE_ADMIN" 和 "ROLE_DBA".  您会注意到,由于我们使用的是 hasRole 表达式,因此不需要指定 "ROLE_" 前缀.
任何尚未匹配的URL都会被拒绝访问.  如果您不想意外忘记更新授权规则,这是一个很好的策略.

1. 基于表达式的访问控制

Spring Security 3.0引入了使用Spring EL表达式作为授权机制的能力,此外还可以简单地使用配置属性和访问决定投票器. 基于表达式的访问控制基于相同的体系结构,但允许将复杂的布尔逻辑封装在单个表达式中.
常见的内置表达式

方法安全性表达式

方法安全性比简单的允许或拒绝规则要复杂一些. 为了提供对表达式使用的全面支持,Spring Security 3.0引入了一些新的注解.

@Pre 和 @Post 注解

有四个注解支持表达式属性,以允许调用前和调用后的授权检查,还支持过滤提交的集合参数或返回值. 它们是 @PreAuthorize,@PreFilter,@PostAuthorize 和 @PostFilter. 通过 global-method-security 命名空间元素启用它们的使用:

<global-method-security pre-post-annotations="enabled"/>

使用 @PreAuthorize 和 @PostAuthorize 的访问控制

最明显有用的注解是 @PreAuthorize,它决定是否可以实际调用方法. 例如

@PreAuthorize("hasRole('USER')")
public void create(Contact contact);

这意味着只有角色为 "ROLE_USER" 的用户才能访问. 显然,使用传统配置和所需角色的简单配置属性可以轻松实现同一目标.
不太常见的是,可能希望在调用该方法之后执行访问控制检查. 这可以使用 @PostAuthorize 注解来实现.

2. 开启方法安全

可以在任何 @Configuration 实例上使用 @EnableGlobalMethodSecurity 注解启用基于注解的安全性. 例如,以下将启用Spring Security的 @Secured 注解.

@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}

Spring MVC 整合

  1. 要启用与Spring MVC的Spring Security集成,请在配置中添加 @EnableWebSecurity 注解.
    创建Spring Security Java配置. 该配置将创建一个称为 springSecurityFilterChain 的Servlet过滤器,该过滤器负责应用程序内的所有安全性 (保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等) . 下面是Spring Security Java配置的最基本示例:
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
        return manager;
    }
}
  1. HttpSecurity
    WebSecurityConfigurerAdapter 配置类. 它具有一种名为 configure 的方法,具有以下默认实现
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests(authorize -> authorize
            .anyRequest().authenticated()
        )
        .formLogin(withDefaults())
        .httpBasic(withDefaults());
}

上面的默认配置:

  • 确保对我们应用程序的任何请求都需要对用户进行身份验证

  • 允许用户使用基于表单的登录进行身份验证

  • 允许用户使用HTTP Basic身份验证进行身份验证

使用

  1. 引入
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
  1. 编写 Spring Security 配置类
    文档:Spring Security文档
/**
 * SpringSecurity配置
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
                .disable()
                .sessionManagement()// 基于token,所以不需要session
//stateless – Spring Security不会创建session,或使用session; 
//always – 如果session不存在总是需要创建;
//ifRequired – 仅当需要时,创建session(默认配置);
//never – 框架从不创建session,但如果已经存在,会使用该session ;            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-resources/**",
                        "/v2/api-docs/**"
                )
                .permitAll()
                .antMatchers("/admin/login", "/admin/register")// 对登录注册要允许匿名访问
                .permitAll()
                .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
                .permitAll()
//                .antMatchers("/**")//测试时全部运行访问
//                .permitAll()
                .anyRequest()// 除上面外的所有请求全部需要鉴权认证
                .authenticated();
        // 禁用缓存
        httpSecurity.headers().cacheControl();
        // 添加JWT filter
        httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

Q.E.D.