Spring Security: Getting Started With The HttpSecurity
作者:禅与计算机程序设计艺术
1.简介
自19年6月1日起, Spring Framework正式推出了其5.2版本,这一里程碑事件对Spring开发人员具有重要意义。在过去的某个时间段内, Spring Security随之发布了新的5.2.0版本,整合了多项新功能与改进措施。然而,尽管如此,许多开发者仍持保留态度,普遍认为该框架已不再适用于现代应用环境,并建议避免将其部署于生产系统中使用。他们相信现有技术栈中的安全组件往往存在过时、脆弱或存在漏洞的问题。另一些开发者则表现出对该框架的喜爱,但对其实际操作尚缺乏深入经验与技能支持。为了帮助解决相关问题,我将通过介绍Spring Security的HTTP安全基础功能,演示如何配置HttpSecurity bean以实施安全策略和保护Web应用程序。本教程由Baeldung编写团队独立完成并优化整理而成
注:这些潜在威胁可能造成巨大的经济损失和社会不稳定
注:此处"工具"指代什么呢?这里可能需要进一步澄清
注:原文中的"两类主要的安全性问题"是否需要进一步解释?
注:原文中的"授权决定用户拥有的权限"是否需要进一步说明?
注:此处是否有必要补充更多细节以增强理解
-
Java编程语言
-
Maven构建工具
-
Spring Boot
-
JSON Web Tokens (JWT)
-
HTTP协议
-
RESTful API
-
OAuth 2.0
-
JWT编码规范
-
JSON Web Signatures(JWS)
-
X.509数字证书
-
HTTPS
另外,由于本教程涵盖了较为复杂的内容,所以要求读者具有良好的计算机科学基础,能够理解各种算法及其工作原理。
下面让我们开始吧!
3.Spring Security介绍
Spring Security是一个开源的Java平台框架,用来构建健壮的、基于角色的安全应用(如Web应用),并集成到现有的安全框架(如Apache Shiro)。Spring Security带有一个全面的安全功能集,包括身份验证、授权、加密传输、访问控制以及密码管理等。
Spring Security的设计目标是成为Spring生态系统中的一个重要的模块。Spring Security支持基于注解的声明式安全,并提供对WebFlux的Reactive支持。Spring Security提供了对OAuth 2.0、JSON Web Tokens(JWT)、OpenID Connect和SAML等多种安全机制的支持。它还有一个广泛的生态系统,包括许多第三方库和插件,可用于扩展它的功能。
概括来说,Spring Security提供了以下几个主要功能: -
Authentication:身份验证,该功能负责验证用户凭据并颁发安全令牌,其中包括用户主体标识符。
-
Authorization:授权,该功能根据用户的角色和权限授予访问控制列表,以允许用户访问受保护的资源。
-
Cryptography:加密,该功能支持对加密敏感数据的安全存储。
-
Caching:缓存,该功能支持在内存中缓存认证结果。
-
ACL(Access Control Lists):访问控制列表,该功能支持将资源划分为命名空间,并向不同类型的用户授予适当级别的访问权限。
-
Session Management:会话管理,该功能支持创建有效期限短的会话,并限制用户可以访问的资源数量。
-
OIDC (OpenID Connect):OpenID Connect提供了一个全面的身份验证解决方案,可支持包括Facebook、GitHub、Google和Microsoft在内的众多身份提供商。
-
Remember-me:记住我的功能,该功能允许用户在退出浏览器后重新登录,而无需提供用户名和密码。
-
Password management:密码管理,该功能支持用户设置强密码和密码过期策略。
-
CORS(Cross-Origin Resource Sharing):跨域资源共享,该功能支持跨域资源共享(CORS)请求。
-
CSRF(Cross-Site Request Forgery):跨站点请求伪造,该功能支持防止攻击者冒充受信任用户进行恶意操作。
-
Exception Translation:异常转换,该功能支持将运行时异常转换为相应的HTTP错误响应。
-
Filter Chaining:过滤器链,该功能支持配置多个过滤器,并按顺序执行它们。
-
Port Proxying:端口代理,该功能支持运行在容器之外的应用,可以通过容器暴露的接口提供服务。
-
Remember Me Token:记住我的令牌,该功能支持安全地保存用户的认证状态。
4.基本概念和术语
在本教程中,我们将学习Spring Security的HTTP安全支持,因此我们首先需要了解一些核心的概念和术语。
身份验证(Authentication)
用户身份验证是确认用户真实身份的过程。Spring Security的HTTP安全支持包括认证、授权和会话管理功能。Spring Security采用一套预定义的认证方案,这些方案涵盖了最常用的方法,例如表单身份验证、HTTP Basic和HTTP Digest、X.509客户端证书身份验证、OpenID Connect、JSON Web Tokens(JWT)等。
授权(Authorization)
授权是指在已获授权的情况下允许用户访问特定的资源,如服务器上的特定页面、文件或API。Spring Security支持多种形式的授权,包括基于角色的访问控制(Role-based Access Control,RBAC)、属性驱动的访问控制(Attribute-based Access Control,ABAC)以及通用表达式(General Expression Language,GEL)等。
会话管理(Session Management)
会话管理是指在用户与服务器之间维持用户会话的过程。Spring Security的HTTP安全支持包括自动身份验证、基于URL的会话固定和记住我的功能。
加密(Cryptography)
加密是指将数据转化为可读不可复原的格式,通常称作加密密钥。Spring Security支持各种加密算法,包括AES、RSA、HMAC、PBKDF2 Hashing、bcrypt Hashing等。
访问控制列表(ACL)
访问控制列表是由一系列权限组成的列表,用于定义哪些用户、组或其他实体可以访问特定资源。Spring Security提供ACL抽象层,让开发人员可以轻松配置ACL并将其与Spring Security的其他功能结合起来。
跨域资源共享(CORS)
跨域资源共享(CORS)是一种标准,它允许某些资源从不同的源(域名、协议、端口号)获取资源。Spring Security的HTTP安全支持包括对CORS的完全支持。
跨站点请求伪造(CSRF)
跨站点请求伪造(CSRF)是一种恶意网站利用受害者的登录凭证,非法执行某项操作的攻击行为。Spring Security的HTTP安全支持包括对CSRF的完全支持。
记住我(Remember me)
“记住我”功能是指用户可以在一次登录之后,在特定时间段内不需要再次输入用户名和密码。Spring Security的HTTP安全支持包括记住我功能。
密码管理(Password management)
密码管理是指用户管理自己的密码的过程。Spring Security的HTTP安全支持包括密码校验、密码强度评估、密码历史记录和密码重置功能。
JSON Web Tokens(JWT)
JSON Web Tokens(JWT)是一种在双方通信过程中用于传递声明的紧凑且自包含的信息。Spring Security支持JWT作为认证凭证。
JSON Web Signatures(JWS)
JSON Web Signatures(JWS)是一种基于JSON的签名格式,它可以验证内容是否被篡改并获得签名者的相关信息。
OAuth 2.0
OAuth 2.0是一个行业标准,它是一种基于授权的授权协议。Spring Security支持OAuth 2.0作为身份验证和授权方案。
X.509数字证书
X.509数字证书是用于确认证书所有权和绑定到身份的数字证书。Spring Security的HTTP安全支持包括对X.509数字证书的完全支持。
HTTPS
HTTPS是安全通道建立的协议,它在网络上传输的所有信息都被加密,只有接收方才能解密。Spring Security的HTTP安全支持包括HTTPS支持。
5.基本设置
在继续阅读之前,我们需要确保我们已经设置好了Spring Boot项目的环境,包括Maven构建工具,Spring Boot Starter Security和Spring Web。如果没有的话,可以使用下面的命令进行安装:
$ mkdir spring-security && cd spring-security
$ mvn archetype:generate \
-DarchetypeGroupId=org.springframework.boot \
-DarchetypeArtifactId=spring-boot-starter-web \
-DgroupId=com.example \
-DartifactId=demo \
-Dversion=0.0.1-SNAPSHOT \
-Dpackage=com.example.demo
$ nano pom.xml # add dependencies for security and webmvc
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
...
<!-- For adding webMVC support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
代码解读
接着,创建一个配置文件application.properties,添加如下内容:
server.port=8080
spring.security.user.name=yourusernamehere
spring.security.user.password=<PASSWORD>
代码解读
请记住,请将yourusernamehere和<PASSWORD>这两个变量替换成实际的用户名和密码。另外,在类路径下的配置文件application.properties中进行如下设置:注:由于平台限制,请确保所有敏感信息已安全处理。
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
代码解读
配置完成后,就可以启动应用了:
$./mvnw spring-boot:run
代码解读
如果一切顺利,应该看到Spring Boot的默认欢迎界面。
6.配置文件详解
上面的例子仅仅简单地展示了如何使用配置文件来设置安全认证。但是Spring Security还提供了许多高级的配置选项。下面让我们详细地探讨一下。
配置Spring Security
Spring Security的配置文件位于resources/application.properties中。下面列举了一些最重要的配置选项:
security.oauth2.client.registration.[registration_id].client-id
设置OAuth 2.0客户端ID。
security.oauth2.client.registration.[registration_id].client-secret
设置OAuth 2.0客户端秘钥。
security.oauth2.client.provider.[provider_id].issuer-uri
设置OAuth 2.0 Issuer URI。
security.oauth2.resource.jwt.key-value
设置JSON Web Token(JWT)的公钥/私钥值对。
server.servlet.session.cookie.http-only
是否只允许Cookie被JS脚本读取。
spring.security.authentication.remember-me.key
设置RememberMe cookie key。
spring.security.filter.order
指定过滤器的顺序。
spring.security.headers.content-security-policy
设置Content Security Policy头部。
spring.security.headers.content-security-policy.report-uri
设置报告URI地址。
spring.security.headers.frame-options
设置Frame Options头部。
spring.security.headers.hsts
设置HTTP Strict Transport Security头部。
spring.security.headers.xss-protection
设置XSS Protection头部。
spring.security.sessions
是否开启Session管理。
当我们修改了配置文件后,Spring Boot不会立即生效,我们需要手动重新启动应用才能使更改生效。
7.HttpSecurity Bean配置
Spring Security的HTTP安全支持是基于HttpSecurity bean的,它是一种基于注释的DSL(Domain Specific Language)风格,使得安全配置变得很容易。下面是一个简单的示例:
@EnableWebSecurity // Enable the Spring Security configuration for the application
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll(); // Allow access to all other URLs without authentication
}
}
代码解读
上述代码创建了一个新的HttpSecurity配置类,并继承了WebSecurityConfigurerAdapter。它覆写了父类的configure()方法,在此方法中,我们调用了HttpSecurity对象上的authorizeRequests()方法,配置了两个匹配模式。第一个匹配模式(/api/**),允许经过身份验证的用户访问;第二个匹配模式(/),允许所有用户访问。除此之外,还有很多其他的配置选项,比如设定登录页,关闭CSRF保护等。
除了以上配置,Spring Security还支持自定义身份验证、授权和授权策略。我们可以自己实现不同的策略,也可以直接使用Spring Security提供的各种身份验证方案。下面我们来看一下Spring Security的身份验证方案。
8.身份验证方案
Spring Security支持多种身份验证方案,包括表单身份验证、HTTP Basic和HTTP Digest、X.509客户端证书身份验证、OpenID Connect、JSON Web Tokens(JWT)等。下面让我们详细介绍一下。
表单身份验证(Form Login)
表单身份验证是最简单的身份验证方案。它要求用户填写登录表单,然后提交表单给Servlet容器。在Spring Security中,我们可以直接使用@EnableWebSecurity注解开启表单身份验证,并指定登录页面的URL:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login**", "/logout**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error");
}
}
代码解读
如前所述,在我们的应用中我们使用HttpSecurity对象调用了formLogin()方法,并配置了 login 页面路径。当用户试图访问受保护的页面时,在未成功 login 的情况下会转至指定的成功 login 页,在 login 失败的情况下则会转至指定的成功 failure 页面。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.realmName("MyApp")
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
代码解读
如上所述,在HttpSecurity对象中调用了httpBasic()方法,并配置了身份验证 realm name以确保安全通信。当系统检测到用户访问受保护资源时,默认情况下若未进行身份认证则会自动跳转至认证页面进行身份验证。
基于X.509标准的客户端证书身份验证是一种利用SSL/TLS协议实现安全通信的认证方式。
Spring Security 支持基于X.509证书的身份认证机制,该方法可用来授权客户端设备连接至服务器以确保通信安全性,具体配置方式如下所示:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.x509()
.subjectPrincipalRegex("CN=(.*?),OU=(.*?),O=(.*?),L=(.*?),ST=(.*?),C=(.*)")
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
代码解读
如上所述,在实现安全机制时,我们调用了HttpSecurity对象的x509()方法,并配置了用于解析主题名称的正则表达式参数。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder decoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation("https://idp.example.com/.well-known/openid-configuration");
return decoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder())
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
代码解读
在前面的部分中, 我们在之前的部分中创建了一个JwtDecoder Bean, 该Bean负责解码JWT token信息。随后, 我们通过HttpSecurity实现了OAuth 2.0资源服务器功能, 并配置了JSON Web Token解析器以处理OAuth 2.0授权请求。最后, 我们设置了严格的权限控制机制, 只允许经过身份验证认证的用户访问被保护的页面内容
Spring Security实现了JSON Web Tokens (JWT)的身份认证功能。作为一种高度浓缩且自-contained、互换性良好的JSON Web Token, JWT能够安全地携带用户数据进行传输和验证操作。Spring Security不仅支持读取这些Token值, 还能够对它们进行严格的认证验证
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder decoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation("https://idp.example.com/.well-known/openid-configuration");
return decoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder())
.and()
.authorizeRequests()
.antMatchers("/api/**").hasAuthority("SCOPE_admin")
.anyRequest().permitAll();
}
}
代码解读
如上所示,我们定义了一个JwtDecoder Bean,用于解析JWT token。然后,我们调用了HttpSecurity对象的oauth2ResourceServer()方法,并调用jwt()方法,配置了JWT Token解析器。最后,我们配置了授权规则,只有具备SCOPE_admin权限的用户才可以访问受保护的页面。
9.授权策略
Spring Security支持多种授权策略,包括基于角色的访问控制(Role-based Access Control,RBAC)、属性驱动的访问控制(Attribute-based Access Control,ABAC)以及通用表达式(General Expression Language,GEL)等。下面我们来看一下Spring Security的授权策略。
RBAC授权策略
Spring Security的RBAC授权策略是最简单的授权策略,它将用户和角色进行映射。在Spring Security中,我们可以像下面这样配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admins/*").access("hasRole('ADMIN')")
.antMatchers("/users/*").access("hasRole('USER')")
.antMatchers("/public/*").permitAll()
.anyRequest().denyAll();
}
}
代码解读
属性驱动型的安全策略(ABAC)是一种比基于角色的信任模型(RBAC)更加细致和灵活的安全策略。其核心思想是根据参与者的属性特征来动态地决定其具有的权利与责任。在Spring Security框架中,默认情况下系统会自动实现这一功能机制;如果需要自定义配置则需自行编写相关的业务逻辑代码来扩展这一框架的功能
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((requests) -> requests
.antMatchers("/orders/{order}")
.access("@accessControlManager.checkOrder(#order)")
)
.expressionHandler(new DefaultWebExpressionHandler());
}
}
代码解读
如上所示,我们调用了HttpSecurity对象的authorizeRequests()方法,传入了一个匿名内部类,配置了访问订单的匹配模式和自定义的授权表达式。授权表达式可以使用Spring EL语法,它会调用AccessControlManager类的checkOrder()方法来判定用户是否有权访问指定的订单。
GEL授权策略
通用表达式语言(General Expression Language,GEL)是一种灵活的表达式语言,它允许自定义授权策略。在Spring Security中,我们可以像下面这样配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests((requests) -> requests
.antMatchers("/documents/private/**")
.access("#currentUser.username=='john'")
);
http.headers().defaultsDisabled(); // Disable default security headers before enabling custom ones
http.headers().contentSecurityPolicy("default-src'self'"); // Add a content security policy header
http.csrf().disable(); // Disable csrf protection before enabling it on certain endpoints only
}
}
代码解读
正如所述,在调用HttpSecurity对象的功能时(...),我们配置了一个匿名类来实现对敏感资源访问权限的控制,并设置了自定义的安全权限表达式。这些权限表达式可采用基于SpEL(Simple Policy Expression Language)的方式构建,并将被当前用户的Security beneficial字段所引用以确保合规性要求。此外,在移除了默认的安全头之后(...),我们新增引入了一份Content Security Policy头文件以进一步强化安全策略的实施范围。
