在Spring web应用中使用Spring Security

https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#web.security

如果Spring Security在类路径中,则Web应用程序将默认受到保护。Spring Boot依赖于Spring Security的内容协商策略来确定是否使用httpBasicformLogin。要向Web应用程序添加方法级别的安全性,还可以添加@EnableGlobalMethodSecurity,并设置你希望使用的配置。

默认的UserDetailsService有一个用户。用户名为user,密码是随机的,并在应用程序启动时以WARN级别打印出来,如下所示:

Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35

This generated password is for development use only. Your security configuration must be updated before running your application in production.

注意:如果你调整了日志配置,请确保将org.springframework.boot.autoconfigure.security类别设置为记录WARN级别的消息。否则,默认密码将不会打印出来。

可以通过提供spring.security.user.namespring.security.user.password来更改用户名和密码。

在Web应用程序中,默认获得的基本功能包括:

  • 一个UserDetailsService(对于WebFlux应用程序,则为ReactiveUserDetailsService)bean,具有内存存储和一个带有生成密码的单个用户。
  • 对于整个应用程序(如果actuator在类路径中,则包括actuator端点)的基于表单的登录或HTTP Basic安全(取决于请求中的Accept标头)。
  • 一个DefaultAuthenticationEventPublisher,用于发布身份验证事件。

可以通过为它添加一个bean来提供一个不同的AuthenticationEventPublisher

MVC安全(MVC Security)

默认的安全配置在SecurityAutoConfigurationUserDetailsServiceAutoConfiguration中实现。SecurityAutoConfiguration导入SpringBootWebSecurityConfiguration用于Web安全,而UserDetailsServiceAutoConfiguration配置身份验证,这也适用于非Web应用程序。

要完全关闭默认的Web应用程序安全配置,或者要组合多个Spring Security组件(如OAuth2客户端和资源服务器),请添加一个类型为SecurityFilterChain的bean(这样做不会禁用UserDetailsService配置或Actuator的安全性)。要关闭UserDetailsService配置,可以添加一个类型为UserDetailsServiceAuthenticationProviderAuthenticationManager的bean。

UserDetailsService的自动配置还将在以下任何Spring Security模块位于类路径中时停止:

  • spring-security-oauth2-client
  • spring-security-oauth2-resource-server
  • spring-security-saml2-service-provider

要在这些依赖项之一或多个之外使用UserDetailsService,请定义自己的InMemoryUserDetailsManager bean。

可以通过添加自定义的SecurityFilterChain bean来覆盖访问规则。Spring Boot提供了一些便捷方法,可用于覆盖actuator端点和静态资源的访问规则。可以使用EndpointRequest创建一个基于management.endpoints.web.base-path属性的RequestMatcher。可以使用PathRequest为常用位置的资源创建一个RequestMatcher

WebFlux 安全

与Spring MVC应用程序类似,可以通过添加spring-boot-starter-security依赖项来保护你的WebFlux应用程序。默认的安全配置在ReactiveSecurityAutoConfigurationUserDetailsServiceAutoConfiguration中实现。ReactiveSecurityAutoConfiguration导入WebFluxSecurityConfiguration用于Web安全,而UserDetailsServiceAutoConfiguration配置身份验证,这也适用于非Web应用程序。

要完全关闭默认的Web应用程序安全配置,可以添加一个类型为WebFilterChainProxy的bean(这样做不会禁用UserDetailsService配置或Actuator的安全性)。要关闭UserDetailsService配置,可以添加一个类型为ReactiveUserDetailsServiceReactiveAuthenticationManager的bean。

当以下任何Spring Security模块位于类路径中时,自动配置也将停止:

  • spring-security-oauth2-client
  • spring-security-oauth2-resource-server

要在这些依赖项之一或多个之外使用ReactiveUserDetailsService,请定义自己的MapReactiveUserDetailsService bean。

可以通过添加自定义的SecurityWebFilterChain bean来配置访问规则和使用多个Spring Security组件(如OAuth 2客户端和资源服务器)。Spring Boot提供了一些便捷方法,可用于覆盖actuator端点和静态资源的访问规则。可以使用EndpointRequest创建一个基于management.endpoints.web.base-path属性的ServerWebExchangeMatcher

可以使用PathRequest为常用位置的资源创建一个ServerWebExchangeMatcher

例如,可以通过添加以下内容来定制你的安全配置:

import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration(proxyBeanMethods = false)
public class MyWebFluxSecurityConfiguration {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange((exchange) -> {
            exchange.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
            exchange.pathMatchers("/foo", "/bar").authenticated();
        });
        http.formLogin(withDefaults());
        return http.build();
    }

}

OAuth2

OAuth2是一个广泛使用的鉴权框架,它受到Spring的支持。

客户端(Client)

如果你的类路径上有spring-security-oauth2-client,则可以利用一些自动配置来设置OAuth2/OpenID Connect客户端。此配置使用OAuth2ClientProperties下的属性。相同的属性适用于Servlet和响应式应用程序。

可以在spring.security.oauth2.client前缀下注册多个OAuth2客户端和提供者(provider ),如下所示:

spring.security.oauth2.client.registration.my-login-client.client-id=abcd
spring.security.oauth2.client.registration.my-login-client.client-secret=password
spring.security.oauth2.client.registration.my-login-client.client-name=Client for OpenID Connect
spring.security.oauth2.client.registration.my-login-client.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-login-client.scope=openid,profile,email,phone,address
spring.security.oauth2.client.registration.my-login-client.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.my-login-client.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-login-client.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri={baseUrl}/authorized/user
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri={baseUrl}/authorized/email
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server.com/oauth2/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server.com/oauth2/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server.com/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server.com/oauth2/jwks
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name

对于支持OpenID Connect discovery 的OpenID Connect Provider,可以进一步简化配置。提供者需要使用issuer-uri进行配置,该issuer-uri是它所声称的发行者标识符的URI。例如,如果提供的issuer-uri是“https://example.com”,则将向“https://example.com/.well-known/openid-configuration”发出“OpenID Provider Configuration Request”。预期结果是“OpenID Provider Configuration Response”。以下示例显示了如何使用issuer-uri配置OpenID Connect Provider:

spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/

默认情况下,Spring Security的OAuth2LoginAuthenticationFilter仅处理与/login/oauth2/code/*匹配的URL。如果你想自定义redirect-uri以使用不同的模式,则需要提供配置来处理该自定义模式。例如,对于Servlet应用程序,可以添加类似于以下内容的自定义SecurityFilterChain

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class MyOAuthClientConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().authenticated()
            )
            .oauth2Login((login) -> login
                .redirectionEndpoint((endpoint) -> endpoint
                    .baseUri("/login/oauth2/callback/*")
                )
            );
        return http.build();
    }

}

提示:Spring Boot自动配置一个InMemoryOAuth2AuthorizedClientService,该服务由Spring Security用于管理客户端注册。InMemoryOAuth2AuthorizedClientService的功能有限,仅推荐在开发环境中使用。对于生产环境,请考虑使用JdbcOAuth2AuthorizedClientService或创建你自己的OAuth2AuthorizedClientService实现。

常见提供商的OAuth2客户端注册

对于常见的OAuth2和OpenID提供商,包括Google、Github、Facebook和Okta,Spring Boot提供了一组提供商默认值(分别是googlegithubfacebookokta)。

如果你不需要自定义这些提供商,可以将provider属性设置为需要推断默认值的提供商。此外,如果客户端注册的键与受支持的默认提供商匹配,Spring Boot也会进行推断。
换句话说,以下示例中的两种配置都使用Google提供商:

spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password

资源服务器

如果你的类路径上有spring-security-oauth2-resource-server,则Spring Boot可以设置OAuth2资源服务器。对于JWT配置,需要指定JWK Set URI或OIDC Issuer URI,如下所示:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/

注意:如果授权服务器不支持JWK Set URI,你可以使用用于验证JWT签名的公钥配置资源服务器。这可以通过使用spring.security.oauth2.resourceserver.jwt.public-key-location属性来完成,该属性的值需要指向一个包含PEM编码x509格式公钥的文件。

可以使用spring.security.oauth2.resourceserver.jwt.audiences属性来指定JWT中aud声明的预期值。例如,要求JWT包含值为my-audience的aud声明:

spring.security.oauth2.resourceserver.jwt.audiences[0]=my-audience

相同的属性适用于Servlet和响应式应用程序。或者,你可以为Servlet应用程序定义自己的JwtDecoder bean,或为响应式应用程序定义ReactiveJwtDecoder

在不使用JWT而使用不透明令牌的情况下,你可以配置以下属性以通过内省验证令牌:

spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret

同样,相同的属性适用于Servlet和响应式应用程序。或者,你可以为Servlet应用程序定义自己的OpaqueTokenIntrospector bean,或为响应式应用程序定义ReactiveOpaqueTokenIntrospector

授权服务器(Authorization Server)

如果在你的类路径(classpath)中有spring-security-oauth2-authorization-server,你可以利用一些自动配置来设置一个基于Servlet的OAuth2授权服务器。

你可以在spring.security.oauth2.authorizationserver.client前缀下注册多个OAuth2客户端,如下所示:

spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-id=abcd
spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-secret={noop}secret1
spring.security.oauth2.authorizationserver.client.my-client-1.registration.client-authentication-methods[0]=client_secret_basic
spring.security.oauth2.authorizationserver.client.my-client-1.registration.authorization-grant-types[0]=authorization_code
spring.security.oauth2.authorizationserver.client.my-client-1.registration.authorization-grant-types[1]=refresh_token
spring.security.oauth2.authorizationserver.client.my-client-1.registration.redirect-uris[0]=https://my-client-1.com/login/oauth2/code/abcd
spring.security.oauth2.authorizationserver.client.my-client-1.registration.redirect-uris[1]=https://my-client-1.com/authorized
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[0]=openid
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[1]=profile
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[2]=email
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[3]=phone
spring.security.oauth2.authorizationserver.client.my-client-1.registration.scopes[4]=address
spring.security.oauth2.authorizationserver.client.my-client-1.require-authorization-consent=true
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-id=efgh
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-secret={noop}secret2
spring.security.oauth2.authorizationserver.client.my-client-2.registration.client-authentication-methods[0]=client_secret_jwt
spring.security.oauth2.authorizationserver.client.my-client-2.registration.authorization-grant-types[0]=client_credentials
spring.security.oauth2.authorizationserver.client.my-client-2.registration.scopes[0]=user.read
spring.security.oauth2.authorizationserver.client.my-client-2.registration.scopes[1]=user.write
spring.security.oauth2.authorizationserver.client.my-client-2.jwk-set-uri=https://my-client-2.com/jwks
spring.security.oauth2.authorizationserver.client.my-client-2.token-endpoint-authentication-signing-algorithm=RS256

注意client-secret属性的格式必须能够被配置的PasswordEncoder匹配。默认的PasswordEncoder实例是通过PasswordEncoderFactories.createDelegatingPasswordEncoder()创建的。

Spring Boot为Spring Authorization Server提供的自动配置旨在快速入门。大多数应用程序都需要进行定制,并希望定义一些bean来覆盖自动配置。

以下组件可以作为bean进行定义,以覆盖与Spring Authorization Server相关的特定自动配置:

  • RegisteredClientRepository
  • AuthorizationServerSettings
  • SecurityFilterChain
  • com.nimbusds.jose.jwk.source.JWKSource<com.nimbusds.jose.proc.SecurityContext>
  • JwtDecoder

提示:Spring Boot会自动配置一个InMemoryRegisteredClientRepository,该仓库由Spring Authorization Server用于管理已注册的客户端。InMemoryRegisteredClientRepository的功能有限,推荐仅在开发环境中使用它。对于生产环境,请考虑使用JdbcRegisteredClientRepository或创建自己的RegisteredClientRepository实现。

SAML 2.0

信赖方(Relying Party)

如果在你的类路径(classpath)中有spring-security-saml2-service-provider,你可以利用一些自动配置来设置一个SAML 2.0 Relying Party。这个配置会用到Saml2RelyingPartyProperties下的属性。

信赖方注册代表了一个身份提供商(Identity Provider,IDP)和服务提供商(Service Provider,SP)之间的配对配置。你可以在spring.security.saml2.relyingparty前缀下注册多个信赖方,如下所示:

spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.response-url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.binding=POST
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.sso-url=https://remoteidp1.sso.url

spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.sso-url=https://remoteidp2.sso.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.response-url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.binding=POST

对于SAML2注销,默认情况下,Spring Security的Saml2LogoutRequestFilterSaml2LogoutResponseFilter仅处理与/logout/saml2/slo匹配的URL。如果你想要自定义将AP(Assertion Provider)发起的注销请求发送到的URL或AP发送注销响应到的响应URL,并使用不同的模式,你需要提供配置来处理这个自定义模式。例如,对于Servlet应用程序,你可以添加类似于以下内容的自己的SecurityFilterChain

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration(proxyBeanMethods = false)
public class MySamlRelyingPartyConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
        http.saml2Login(withDefaults());
        http.saml2Logout((saml2) -> saml2.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
            .logoutResponse((response) -> response.logoutUrl("/SLOService.saml2")));
        return http.build();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值