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
.
记住几个类:
- 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 整合
- 要启用与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;
}
}
- HttpSecurity
WebSecurityConfigurerAdapter 配置类. 它具有一种名为 configure 的方法,具有以下默认实现
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
}
上面的默认配置:
-
确保对我们应用程序的任何请求都需要对用户进行身份验证
-
允许用户使用基于表单的登录进行身份验证
-
允许用户使用HTTP Basic身份验证进行身份验证
使用
- 引入
<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>
- 编写 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.