spring - secruity

本文深入探讨Spring Security,一个强大的安全框架,用于处理应用的认证和访问控制。介绍了其核心模块、Java配置、Web安全、授权机制等关键概念,包括配置自定义登录页面、OAuth2登录支持等。同时,展示了数据库模型和ACL模型的示例,帮助开发者理解并掌握Spring Security的使用。
摘要由CSDN通过智能技术生成

下面只介绍了Servlet应用程序的,响应式的未写。由于是翻译的参考文档,有些地方应该词不达意。

1、介绍

Spring Security是一个强大且高度可定制的认证和访问控制框架。它是基于Spring的应用程序的事实上的安全标准。

主要特性:

支持全面和可扩展的身份验证和授权;

防止各种攻击,如会话固定攻击,点击劫持,跨网站请求伪造等;

集成Servlet API;

与SpringMvc的可选集成。

应用程序安全性的两个主要方面是"authentication"和"authorization"(或"access-control")。这是Spring Security的两大主要领域。"Authentication"是建立主体的过程("principal"通常指可在应用程序中执行操作的用户,设备或其它系统)。"Authorization"是指是否允许主体在应用程序内执行操作的过程。为了授权,主体的身份已经由认证过程确定。

在认证级别,Spring Security支持多种认证模式。这些认证模式大多由第三方提供,或者由相关标准组织开发。另外,Spring Security还提供了自己的一套认证功能。

无论Authentication机制如何,Spring Security都提供了一套深层次的授权功能。有三个主要的领域:授权Web请求,授权是否可以调用方法,并授权访问单个域对象实例。为理解这些差异,请分别考虑Servlet规范的Web模式安全性,EJB容器托管安全性和文件系统安全性中的授权功能。Spring Security在所有这些重要领域提供了深入的功能,在本参考指南的后面部分探讨这些功能。

 

2、Security的模块

在Spring Security 3.0中,代码库被细分为独立的jar,这些jar更清楚地区分了不同的功能区域和第三方依赖关系。

  • core--spring-security-core.jar

 

包含核心认证和访问控制类和接口,远程处理支持和基本配置API。由使用Spring Security的任何应用程序所要求。支持独立应用程序,远程客户端,方法(服务层)安全性和JDBC用户配置。包含顶级包:

org.springframework.security.core

org.springframework.security.access

org.springframework.security.authentication

org.springframework.security.provisioning

  • Remoting--spring-security-remoting.jar

 

提供与Spring Remoting的集成。除非正在编写一个使用Spring Remoting的远程客户端,否则不需要这个。主包是org.springframework.security.remoting。

  • Web--spring-security-web.jar

 

包含过滤器和相关的网络安全基础架构代码。任何具有servlet API依赖性的东西。如果需要Spring Security Web认证服务和基于URL的访问控制,将需要它。主包是org.springframework.security.web。

  • Config--spring-security-config.jar

 

包含安全命名空间解析代码和Java代码。如果使用Spring Security XML命名空间进行配置或Spring Security的Java代码支持,则需要它。主包是org.springframework.security.config。这些类别都不能直接用于应用程序。

  • LDAP--spring-security-ldap.jar

 

LDAP认证和供应代码。如果需要使用LDAPAuthentication或管理LDAP用户条目,则为必需。顶级包装是org.springframework.security.ldap。

  • OAuth2.0 Core--spring-security-oauth2-core.jar

 

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。

  • OAuth2.0 Client--spring-security-oauth2-client.jar

 

spring-security-oauth2-client.jar是Spring Security对OAuth 2.0授权框架和OpenID Connect Core 1.0的客户端支持。由利用OAuth 2.0 Login和/或OAuth客户端支持的应用程序所需。顶级包装是org.springframework.security.oauth2.client。

  • OAuth2.0 JOSE--spring-security-oauth2-jose.jar

 

spring-security-oauth2-jose.jar包含Spring Security对JOSE(Javascript对象签名和加密)框架的支持。JOSE框架旨在提供一种安全地在各方之间传输声明的方法。它由一系列规范构建而成:

JSON Web令牌(JWT)

JSON Web签名(JWS)

JSON Web加密(JWE)

JSON Web密钥(JWK)

它包含顶级软件包:

org.springframework.security.oauth2.jwt

org.springframework.security.oauth2.jose

  • ACL--spring-security-acl.jar

 

专门的域对象ACL实现。用于将安全性应用于应用程序内的特定域对象实例。顶级包装是org.springframework.security.acls。

  • CAS--spring-security-cas.jar

 

Spring Security的CAS客户端集成。如果想使用CAS单点登录服务器的Spring Security Web认证。顶级包是org.springframework.security.cas。

  • OpenID--spring-security-openid.jar

 

OpenID Web认证支持。用于对外部OpenID服务器进行Authentication。org.springframework.security.openid,需要OpenID4Java。

  • 测试--spring-security-test.jar

 

支持使用Spring Security进行测试。

 

3、Java代码

在Spring 3.1中为Spring Framework添加了对Java代码的一般支持。自Spring Security 3.2以来,已经有了Java代码的支持,使用户无需使用任何XML即可轻松配置Spring Security。

3.1、Hello Web安全

 

第一步,是创建Spring Security Java代码。该配置会创建一个名为springSecurityFilterChain的Servlet过滤器,它负责应用程序中的所有安全性(保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等)。可以在下面找到Spring Security Java Configuration的最基本的例子:

import org.springframework.beans.factory.annotation.Autowired;

 

import org.springframework.context.annotation.*;

import org.springframework.security.config.annotation.authentication.builders.*;

import org.springframework.security.config.annotation.web.configuration.*;

 

@EnableWebSecurity

public class WebSecurityConfig implements WebMvcConfigurer {

 

    @Bean

    public UserDetailsService userDetailsService() throws Exception {

        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

        manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());

        return manager;

    }

}

这个配置有以下功能摘要:

  1. 需要对应用程序中的每个URL进行Authentication;
  2. 为应用程序生成一个登录表单;
  3. 允许具有Username为user和Password为password的用户使用基于表单的Authentication功能;
  4. 允许用户注销;
  5. CSRF攻击预防;
  6. 会话固定保护;
  7. 安全标题集成
    1. 用于严格的传输安全性的HTTP安全请求;
    2. 集成X-Content-Type-Options;
    3. 缓存控制(可由应用程序覆盖,以缓存静态资源);
    4. 集成X-XSS-Protection;
    5. 集成X-Frame-Options以便防止点击劫持;
  8. 与以下Servlet API方法集成;
    1. HttpServletRequest#getRemoteUser();
    2. HttpServletRequest.html#getUserPrincipal();
    3. HttpServletRequest.html#isUserInRole(java.lang.String);
    4. HttpServletRequest.html#login(java.lang.String,java.lang.String);
    5. HttpServletRequest.html#logout();

下一步是在war中注册springSecurityFilterChain。可通过在支持Servlet 3.0+的环境中使用Spring的WebApplicationInitializer完成java代码。Spring Security提供了一个基类AbstractSecurityWebApplicationInitializer,它将确保springSecurityFilterChain注册。

如果没有使用Spring或Spring MVC,则需要将WebSecurityConfig传递给超类,以确保获取到配置。例子:

import org.springframework.security.web.context.*;

 

public class SecurityWebApplicationInitializer

    extends AbstractSecurityWebApplicationInitializer {

 

    public SecurityWebApplicationInitializer() {

        super(WebSecurityConfig.class);

    }

}

SecurityWebApplicationInitializer将执行以下操作:

1、为应用程序中的每个URL自动注册springSecurityFilterChain过滤器;

2、增加一个载入WebSecurityConfig的ContextLoaderListener。

如果在应用程序中使用Spring,可能已经有一个WebApplicationInitializer。应该使用现有的ApplicationContext注册Spring Security。例如,使用Spring MVC,SecurityWebApplicationInitializer将如下所示:

import org.springframework.security.web.context.*;

 

public class SecurityWebApplicationInitializer

    extends AbstractSecurityWebApplicationInitializer {

 

}

这只会为应用程序中的每个URL注册springSecurityFilterChain过滤器。之后,将确保WebSecurityConfig在现有的ApplicationInitializer中加载。例如,如果使用Spring MVC,它将被添加到getRootConfigClasses()。

public class MvcWebApplicationInitializer extends

        AbstractAnnotationConfigDispatcherServletInitializer {

 

    @Override

    protected Class<?>[] getRootConfigClasses() {

        return new Class[] { WebSecurityConfig.class };

    }

 

    // ... other overrides ...

}

3.2、HttpSecurity

 

迄今为止,WebSecurityConfig仅包含如何验证用户的信息。Spring Security如何知道我们想要所有用户进行Authentication?Spring Security如何知道我们想要支持基于表单的Authentication?原来WebSecurityConfigurerAdapter在configure(HttpSecurity http)方法中提供了一个默认配置,如下所示:

protected void configure(HttpSecurity http) throws Exception {

    http

        .authorizeRequests()

            .anyRequest().authenticated()

            .and()

        .formLogin()

            .and()

        .httpBasic();

}

上面的默认配置:

1、确保对应用程序的任何请求都做用户身份认证;

2、允许用户通过登录表单做身份认证;

3、允许用户使用HTTP基本认证做身份认证。

这个配置与XML命名空间配置非常相似:

<http>

    <intercept-url pattern="/**" access="authenticated"/>

    <form-login />

    <http-basic />

</http>

与闭合的XML标签等价的Java代码是使用and()方法表示的,它允许继续配置父级元素。

3.3、Java代码和表单登录

 

由于没有提及任何HTML文件或JSP,因此你可能想知道登录表单从何时被提示登录。由于Spring Security的默认配置没有明确设置登录页面的URL,因此Spring Security会根据启用的功能自动生成一个URL,并使用处理提交的登录的URL的标准值,用户将默认的目标URL登录后发送。

尽管自动生成的登录页面很方便快速启动和运行,但大多数应用程序都希望提供自己的登录页面。为此,可以更新配置,如下:

protected void configure(HttpSecurity http) throws Exception {

    http

        .authorizeRequests()

            .anyRequest().authenticated()

            .and()

        .formLogin()

            .loginPage("/login") <1>

            .permitAll(); <2>

}

<1>,更新后的配置指定了登录页面的位置(/login)。

<2>,必须授权所有用户(即未经身份认证的用户)访问登录页面。formLogin().permitAll()授权所有用户可访问与基于表单登录关联的URL。

下面是一个使用JSP实现的用于当前配置的登录页面示例。

注意:下面的登录页面代表我们当前的配置。如果某些默认设置不能满足需求,可以更新配置。

<c:url value="/login" var="loginUrl"/>

<form action="${loginUrl}" method="post"> <1>

    <c:if test="${param.error != null}"> <2>

        <p>

            Invalid username and password.

        </p>

    </c:if>

    <c:if test="${param.logout != null}"> <3>

        <p>

            You have been logged out.

        </p>

    </c:if>

    <p>

        <label for="username">Username</label>

        <input type="text" id="username" name="username"/> <4>

    </p>

    <p>

        <label for="password">Password</label>

        <input type="password" id="password" name="password"/> <5>

    </p>

    <input type="hidden" <6>

        name="${_csrf.parameterName}"

        value="${_csrf.token}"/>

    <button type="submit" class="btn">Log in</button>

</form>

<1>,通过URL地址/login以POST方式发起请求,以对用户做身份认证;

<2>,如果检查到参数中存在错误,则认证会失败;

<3>,如果检查到参数中有logout,则认为用户已成功注销;

<4>,用户名必须以username的HTTP参数名提供;

<5>,密码必须以password的HTTP参数名提供;

<6>,有一个隐含域是包含CSRF令牌的。

3.4、授权请求

 

示例只需要用户做身份认证,并已为应用程序中的每个URL完成此操作。可以通过向http.authorizeRequests()方法中添加多个子项来为URL指定自定义要求。例如:

protected void configure(HttpSecurity http) throws Exception {

    http

        .authorizeRequests() <1>

            .antMatchers("/resources/**", "/signup", "/about").permitAll() <2>

            .antMatchers("/admin/**").hasRole("ADMIN") <3>

            .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") <4>

            .anyRequest().authenticated() <5>

            .and()

        // ...

        .formLogin();

}

<1>,http.authorizeRequests()方法有多个子项方法,每个matcher按照它们声明的顺序执行。

<2>,指定了任何用户都可以访问的多个URL模式。如果URL以"/resources/",或"/signup",或"/about"开头,那么任何用户都可以访问。

<3>,任何以"/admin/"开头的URL将仅限于拥有"ROLE_ADMIN"角色的用户访问。由于调用了hasRole()方法,因此不需要指定"ROLE_"前缀。

<4>,任何以"/db/"开头的URL都需要用户同时拥有"ROLE_ADMIN"和"ROLE_DBA"角色。由于使用的是hasRole表达式,因此无需指定"ROLE_"前缀。

<5>,任何尚未匹配的URL只需要对用户做身份认证。

3.5、处理注销

 

使用WebSecurityConfigurerAdapter时,会自动应用注销功能。默认情况下,访问URL /logout将通过以下方式注销用户:

1,使HTTP session失效;

2,清除任何已配置的RememberMe认证信息;

3,清除SecurityContextHolder;

4,重定向到/login?logout。

类似于配置登录功能,还可以有多种选项来进一步自定义注销要求:

protected void configure(HttpSecurity http) throws Exception {

    http

        .logout() <1>

            .logoutUrl("/my/logout") <2>

            .logoutSuccessUrl("/my/index") <3>

            .logoutSuccessHandler(logoutSuccessHandler) <4>

            .invalidateHttpSession(true) <5>

            .addLogoutHandler(logoutHandler) <6>

            .deleteCookies(cookieNamesToClear) <7>

            .and()

        ...

}

<1>,提供注销支持,在使用WebSecurityConfigurerAdapter时会自动应用。

<2>,触发注销的URL(默认/logout)。如果启用CSRF保护(默认启用),则请求必须是POST模式。

<3>,发生注销后重定向到的URL。默认是/login?logout。

<4>,指定一个自定义LogoutSuccessHandler。如果指定,则logoutSuccessUrl()被忽略。

<5>,指定在注销时是否使HttpSession无效,默认true。可配置内部实现的SecurityContextLogoutHandler。

<6>,添加一个LogoutHandler。SecurityContextLogoutHandler默认作为最后一个LogoutHandler。

<7>,允许指定在注销成功时删除的cookies名称。这是显式添加CookieClearingLogoutHandler的快捷方式。

注销也可以使用XML命名空间表示法进行配置。

通常,为了自定义注销功能,可以添加LogoutHandler和(或)LogoutSuccessHandler实现。

        1. LogoutHandler

通常,LogoutHandler实现是指那些能够参与注销处理的类,期望调用它们来执行必要的清理工作,因此,它们不应该抛出异常。实现如下:

[PersistentTokenBasedRememberMeServices]

[TokenBasedRememberMeServices]

[CookieClearingLogoutHandler]

[CsrfLogoutHandler]

[SecurityContextLogoutHandler]

与直接提供LogoutHandler实现不同,fluent API还提供些快捷方式,它们在内部提供了各自的LogoutHandler实现。例如,deleteCookies()允许指定在注销成功时删除一个或多个Cookie的名称。与添加CookieClearingLogoutHandler相比,这是一条捷径。

        1. LogoutSuccessHandler

LogoutFilter在成功注销后调用LogoutSuccessHandler,以处理重定向或期望的转发目标。请注意,interface与LogoutHandler几乎相同,但可能会引发异常。

提供以下实现:

[SimpleUrlLogoutSuccessHandler]

[HttpStatusReturningLogoutSuccessHandler]

如上所述,不需要直接指定SimpleUrlLogoutSuccessHandler。fluent API通过设置logoutSuccessUrl()提供了一种快捷方式,这是由SimpleUrlLogoutSuccessHandler在内部设置的。在注销发生后将重定向到提供的URL上,默认是/login?logout。

在REST API类型的场景中,HttpStatusReturningLogoutSuccessHandler可能很有趣。与在成功注销时重定向到URL不同,这个LogoutSuccessHandler允许提供一个要返回的简单HTTP状态码。如果未配置,默认状态码返回200。

3.6、OAuth 2.0登录

 

OAuth 2.0 Login特性为应用程序提供了一种功能,可以让用户通过使用他们在OAuth 2.0 Provider(例如GitHub)或OpenID Connect 1.0 Provider(例如谷歌)的现有帐户登录到应用程序。OAuth 2.0 Login实现了这样的功能:"Login with Google"或"Login with GitHub"。

注意:OAuth 2.0 Login是通过Authorization Code Grant实现的。

        1. ClientRegistration

ClientRegistration是向OAuth 2.0或OpenID Connect 1.0 Provider注册的客户端的展现。

一个客户端注册会包含很多信息,如client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI和其它信息。

ClientRegistration及其属性定义如下:

public final class ClientRegistration {

    private String registrationId;    <1>

    private String clientId;    <2>

    private String clientSecret;    <3>

    private ClientAuthenticationMethod clientAuthenticationMethod;    <4>

    private AuthorizationGrantType authorizationGrantType;    <5>

    private String redirectUriTemplate;    <6>

    private Set<String> scopes;    <7>

    private ProviderDetails providerDetails;

    private String clientName;    <8>

 

    public class ProviderDetails {

        private String authorizationUri;    <9>

        private String tokenUri;    <10>

        private UserInfoEndpoint userInfoEndpoint;

        private String jwkSetUri;    <11>

 

        public class UserInfoEndpoint {

            private String uri;    <12>

            private String userNameAttributeName;    <13>

        }

    }

}

<1>,registrationId:唯一标识`ClientRegistration`的ID。

<2>,clientId:客户端标识符。

<3>,clientSecret:客户端密码。

<4>,clientAuthenticationMethod:用于向Provider验证客户端的方法。支持的值有basic和post。

<5>,authorizationGrantType:OAuth 2.0授权框架定义了4个[授权]类型。支持的值有authorization_code和implicity。

<6>,redirectUriTemplate:在最终用户通过身份认证和授权可访问客户端之后,授权服务器将最终用户的用户代理重定向到客户端被注册的重定向URI上。默认的重定向URI模板是`{baseUrl}/login/oauth2/code/{registrationId}`,它支持URI模板变量。

<7>,scopes:客户端在授权请求流程中请求的范围,例如openid,email或profile。

<8>,clientName:客户端使用的描述性名称。该名称可用于某些特定场景,例如当在自动生成的登录页面中显示客户端名称的时候。

<9>,authorizationUri:用于授权服务器的授权端点URI。

<10>,tokenUri:授权服务器的令牌端点URI。

<11>,jwkSetUri:该URI用于从授权服务器获取[JSON Web Key(JWK)]集合,其中包含用于验证ID令牌的[JSON Web Signature(JWS)]以及可选的UserInfo响应的加密密钥。

<12>,(userInfoEndpoint)uri:UserInfo端点URI,用于访问经过身份认证的最终用户的声明/属性。

<13>,userNameAttributeName:在UserInfo响应中返回的属性名称,该名称引用最终用户的名称或标识符。

        1. Spring Boot 2.0属性映射

下表概述了Spring Boot 2.0 OAuth客户端属性到ClientRegistration属性的映射。

Spring Boot 2.0

ClientRegistration

spring.security.oauth2.client.registration.[registrationId]

registrationId

spring.security.oauth2.client.registration.[registrationId].client-id

clientId

spring.security.oauth2.client.registration.[registrationId].client-secret

clientSecret

spring.security.oauth2.client.registration.[registrationId].client-authentication-method

clientAuthenticationMethod

spring.security.oauth2.client.registration.[registrationId].authorization-grant-type

authorizationGrantType

spring.security.oauth2.client.registration.[registrationId].redirect-uri-template

redirectUriTemplate

spring.security.oauth2.client.registration.[registrationId].scope

scopes

spring.security.oauth2.client.registration.[registrationId].client-name

clientName

spring.security.oauth2.client.provider.[providerId].authorization-uri

providerDetails.authorizationUri

spring.security.oauth2.client.provider.[providerId].token-uri

providerDetails.tokenUri

spring.security.oauth2.client.provider.[providerId].jwk-set-uri

providerDetails.jwkSetUri

spring.security.oauth2.client.provider.[providerId].user-info-uri

providerDetails.userInfoEndpoint.uri

spring.security.oauth2.client.provider.[providerId].userNameAttribute

providerDetails.userInfoEndpoint.userNameAttributeName

        1. ClientRegistrationRepository

ClientRegistrationRepository充当OAuth 2.0 / OpenID Connect 1.0 ClientRegistration(s)的存储库。

注意:客户端注册信息最终由关联的授权服务器存储和拥有。该存储库提供了获取主客户端注册信息的子集的能力。

Spring Boot 2.0自动配置将spring.security.oauth2.client.registration.[registrationId]下的每个属性绑定到ClientRegistration的实例,然后在ClientRegistrationRepository内组合每个ClientRegistration实例。

注意:ClientRegistrationRepository的默认实现是InMemoryClientRegistrationRepository。

自动配置还把ClientRegistrationRepository作为@Bean在ApplicationContext中注册,以便应用程序需要时,可用于依赖注入。

示例:

@Controller

public class OAuth2LoginController {

 

    @Autowired

    private ClientRegistrationRepository clientRegistrationRepository;

 

    @RequestMapping("/")

    public String index() {

        ClientRegistration googleRegistration =

            this.clientRegistrationRepository.findByRegistrationId("google");

        ...

        return "index";

    }

}

        1. CommonOAuth2Provider

CommonOAuth2Provider预先定义了一组默认客户端属性,适用于众多知名Provider:Google,GitHub,Facebook和Okta。

例如,authorization-uri,token-uri和user-info-uri不会经常更改Provider。因此,提供默认值以减少所需的配置是有意义的。

如前所述,当配置Google client时,只需要client-id和client-secret属性即可:

spring:

  security:

    oauth2:

      client:

        registration:

          google:

            client-id: google-client-id

            client-secret: google-client-secret

提示:由于registrationId(google)与CommonOAuth2Provider中的GOOGLE enum(不区分大小写)匹配,所以客户端属性的自动默认配置在这里可以无缝地工作。

对于希望指定不同的registrationId的情况(如google-login),仍然可以通过配置provider属性来利用客户端属性的自动默认功能。示例:

spring:

  security:

    oauth2:

      client:

        registration:

          google-login: <1>

            provider: google <2>

            client-id: google-client-id

            client-secret: google-client-secret

<1>,registrationId设置为google-login。

<2>,provider属性设置为google,该属性将利用CommonOAuth2Provider.GOOGLE.getBuilder()中设置的客户端属性的自动默认设置。

        1. 配置自定义提供者属性

有些OAuth 2.0提供者支持多租户,这会为每个租户(或子域)生成不同的协议端点。

例如,向Okta注册的OAuth客户端被分配到特定的子域,并拥有自己的协议端点。

对于这些情况,Spring Boot 2.0提供了以下基本属性以配置自定义提供者属性:spring.security.oauth2.client.provider.[providerId]。

spring:

  security:

    oauth2:

      client:

        registration:

          okta: <1>

            client-id: okta-client-id

            client-secret: okta-client-secret

        provider:

          okta:

            authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize

            token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token

            user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo

            user-name-attribute: sub

            jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys

<1>,基本属性(spring.security.oauth2.client.provider.okta)允许自定义配置协议端点的位置。

        1. 重写(overriding)Spring Boot 2.0自动配置

用于OAuth客户端支持的Spring Boot 2.0自动配置类为OAuth2ClientAutoConfiguration。

它执行以下任务:

  1. 从配置的OAuth客户端属性中注册由ClientRegistration(s)组成的一个ClientRegistrationRepository类(用@Bean注解)。
  2. 提供WebSecurityConfigurerAdapter(用@Configuration注解),并通过httpSecurity.oauth2Login()启用OAuth 2.0登录。

如果需要根据特定要求覆盖自动配置,可以通过以下方式进行:

  1. 注册一个ClientRegistrationRepository(用@Bean注解);
  2. 提供一个WebSecurityConfigurerAdapter;
  3. 完全重写(Override)自动配置属性。
          1. 注册一个ClientRegistrationRepository(@Bean注解)

下例显示如何注册ClientRegistrationRepository(用@Bean注解):

@Configuration

public class OAuth2LoginConfig {

 

    @Bean

    public ClientRegistrationRepository clientRegistrationRepository() {

        return new InMemoryClientRegistrationRepository(this.googleClientRegistration());

    }

 

    private ClientRegistration googleClientRegistration() {

        return ClientRegistration.withRegistrationId("google")

            .clientId("google-client-id")

            .clientSecret("google-client-secret")

            .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)

            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)

            .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")

            .scope("openid", "profile", "email", "address", "phone")

            .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")

            .tokenUri("https://www.googleapis.com/oauth2/v4/token")

            .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")

            .userNameAttributeName(IdTokenClaimNames.SUB)

            .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")

            .clientName("Google")

            .build();

    }

}

          1. 提供WebSecurityConfigurerAdapter

下例显示如何为WebSecurityConfigurerAdapter提供@EnableWebSecurity,并通过httpSecurity.oauth2Login()启用OAuth 2.0登录:

@EnableWebSecurity

public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http

            .authorizeRequests()

                .anyRequest().authenticated()

                .and()

            .oauth2Login();

    }

}

          1. 完全重写自动配置

下例显示如何通过注册ClientRegistrationRepository(用@Bean注解),并提供WebSecurityConfigurerAdapter完全覆盖自动配置。

@Configuration

public class OAuth2LoginConfig {

 

    @EnableWebSecurity

    public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

 

        @Override

        protected void configure(HttpSecurity http) throws Exception {

            http

                .authorizeRequests()

                    .anyRequest().authenticated()

                    .and()

                .oauth2Login();

        }

    }

 

    @Bean

    public ClientRegistrationRepository clientRegistrationRepository() {

        return new InMemoryClientRegistrationRepository(this.googleClientRegistration());

    }

 

    private ClientRegistration googleClientRegistration() {

        return ClientRegistration.withRegistrationId("google")

            .clientId("google-client-id")

            .clientSecret("google-client-secret")

            .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)

            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)

            .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")

            .scope("openid", "profile", "email", "address", "phone")

            .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")

            .tokenUri("https://www.googleapis.com/oauth2/v4/token")

            .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")

            .userNameAttributeName(IdTokenClaimNames.SUB)

            .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")

            .clientName("Google")

            .build();

    }

}

        1. 不使用Springboot2.0的配置

如果不使用Spring Boot 2.0,并且想在CommonOAuth2Provider(例如google)中配置一个预定义的Provider,请应用以下配置:

@Configuration

public class OAuth2LoginConfig {

 

    @EnableWebSecurity

    public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

 

        @Override

        protected void configure(HttpSecurity http) throws Exception {

            http

                .authorizeRequests()

                    .anyRequest().authenticated()

                    .and()

                .oauth2Login();

        }

    }

 

    @Bean

    public ClientRegistrationRepository clientRegistrationRepository() {

        return new InMemoryClientRegistrationRepository(this.googleClientRegistration());

    }

 

    @Bean

    public OAuth2AuthorizedClientService authorizedClientService() {

        return new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository());

    }

 

    private ClientRegistration googleClientRegistration() {

        return CommonOAuth2Provider.GOOGLE.getBuilder("google")

            .clientId("google-client-id")

            .clientSecret("google-client-secret")

            .build();

    }

}

        1. OAuth2AuthorizedClient / OAuth2AuthorizedClientService

OAuth2AuthorizedClient是授权客户端的展现。当最终用户(Resource Owner)授权客户端可访问其受保护的资源时,该客户端就被认为是已授权的。

OAuth2AuthorizedClient用于将OAuth2AccessToken关联到ClientRegistration(客户端)和资源所有者,该资源所有者是主要的被授权最终用户。

OAuth2AuthorizedClientService的主要作用是管理OAuth2AuthorizedClient实例。从开发人员的角度看,它提供了查找与客户端关联的OAuth2AccessToken的功能,以便可以使用它向资源服务器发起请求。

注意,Spring Boot 2.0自动配置在`ApplicationContext`中注册`OAuth2AuthorizedClientService`(用@Bean注解)。

开发人员还可以在ApplicationContext(覆盖Spring Boot 2.0自动配置)中注册OAuth2AuthorizedClientService@Bean,以便能够查找与特定相关的OAuth2AccessToken ClientRegistration(客户端)。示例:

@Controller

public class OAuth2LoginController {

 

    @Autowired

    private OAuth2AuthorizedClientService authorizedClientService;

 

    @RequestMapping("/userinfo")

    public String userinfo(OAuth2AuthenticationToken authentication) {

        // authentication.getAuthorizedClientRegistrationId() returns the

        // registrationId of the Client that was authorized during the Login flow

        OAuth2AuthorizedClient authorizedClient =

            this.authorizedClientService.loadAuthorizedClient(

                authentication.getAuthorizedClientRegistrationId(),

                authentication.getName());

 

        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

 

        ...

 

        return "userinfo";

    }

}

3.7、 认证

 

到目前为止,只看了最基本的身份验证配置。下面看一下配置身份验证的稍微高级一些的选项。

        1. 内存认证

以下是配置多个用户的示例:

@Bean

public UserDetailsService userDetailsService() throws Exception {

    // ensure the passwords are encoded properly

    UserBuilder users = User.withDefaultPasswordEncoder();

    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

    manager.createUser(users.username("user").password("password").roles("USER").build());

    manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());

    return manager;

}

        1. JDBC认证

以下示例假定,已经在应用程序中定义了DataSource。

@Autowired

private DataSource dataSource;

 

@Autowired

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    // ensure the passwords are encoded properly

    UserBuilder users = User.withDefaultPasswordEncoder();

    auth

        .jdbcAuthentication()

            .dataSource(dataSource)

            .withDefaultSchema()

            .withUser(users.username("user").password("password").roles("USER"))

            .withUser(users.username("admin").password("password").roles("USER","ADMIN"));

}

        1. LDAP认证

基于LDAP的认证。

@Autowired

private DataSource dataSource;

 

@Autowired

public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    auth

        .ldapAuthentication()

            .userDnPatterns("uid={0},ou=people")

            .groupSearchBase("ou=groups");

}

上面的示例使用了以下LDIF和一个嵌入式Apache DS LDAP实例。

users.ldif。

dn: ou=groups,dc=springframework,dc=org

objectclass: top

objectclass: organizationalUnit

ou: groups

 

dn: ou=people,dc=springframework,dc=org

objectclass: top

objectclass: organizationalUnit

ou: people

 

dn: uid=admin,ou=people,dc=springframework,dc=org

objectclass: top

objectclass: person

objectclass: organizationalPerson

objectclass: inetOrgPerson

cn: Rod Johnson

sn: Johnson

uid: admin

userPassword: password

 

dn: uid=user,ou=people,dc=springframework,dc=org

objectclass: top

objectclass: person

objectclass: organizationalPerson

objectclass: inetOrgPerson

cn: Dianne Emu

sn: Emu

uid: user

userPassword: password

 

dn: cn=user,ou=groups,dc=springframework,dc=org

objectclass: top

objectclass: groupOfNames

cn: user

uniqueMember: uid=admin,ou=people,dc=springframework,dc=org

uniqueMember: uid=user,ou=people,dc=springframework,dc=org

 

dn: cn=admin,ou=groups,dc=springframework,dc=org

objectclass: top

objectclass: groupOfNames

cn: admin

uniqueMember: uid=admin,ou=people,dc=springframework,dc=org

        1. AuthenticationProvider

通过将自定义的AuthenticationProvider类公开为bean来定义自定义的认证类。例如,假设SpringAuthenticationProvider类实现了AuthenticationProvider接口,那么下面的代码就是自定义的身份认证:

注:仅当AuthenticationManagerBuilder尚未填充时才会使用。

@Bean

public SpringAuthenticationProvider springAuthenticationProvider() {

    return new SpringAuthenticationProvider();

}

        1. UserDetailsService

通过将自定义UserDetailsService类公开为bean来定义自定义认证类。例如,假设SpringDataUserDetailsService类实现了UserDetailsService接口,那么,下面的代码就是自定义的身份认证:

注意:仅当AuthenticationManagerBuilder尚未填充且未定义AuthenticationProviderBean时才会使用此选项。

@Bean

public SpringDataUserDetailsService springDataUserDetailsService() {

    return new SpringDataUserDetailsService();

}

还可以通过将PasswordEncoder类公开为bean来定制密码的编码方式。例如,如果使用bcrypt,则可以添加如下所示的一个bean定义:

@Bean

public BCryptPasswordEncoder passwordEncoder() {

    return new BCryptPasswordEncoder();

}

3.8、 多个HttpSecurity

 

可以配置多个HttpSecurity实例,就像可以有多个<http>块一样。其中的关键是对WebSecurityConfigurationAdapter做多次扩展。例如,以下是针对以/api/开头的URL进行不同配置的示例。

@EnableWebSecurity

public class MultiHttpSecurityConfig {

    @Bean   <1>

    public UserDetailsService userDetailsService() throws Exception {

        // ensure the passwords are encoded properly

        UserBuilder users = User.withDefaultPasswordEncoder();

        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

        manager.createUser(users.username("user").password("password").roles("USER").build());

        manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());

        return manager;

    }

 

    @Configuration

    @Order(1)   <2>

    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        protected void configure(HttpSecurity http) throws Exception {

            http

                .antMatcher("/api/**")   <3>

                .authorizeRequests()

                    .anyRequest().hasRole("ADMIN")

                    .and()

                .httpBasic();

        }

    }

 

    @Configuration   <4>

    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

 

        @Override

        protected void configure(HttpSecurity http) throws Exception {

            http

                .authorizeRequests()

                    .anyRequest().authenticated()

                    .and()

                .formLogin();

        }

    }

}

<1>,正常配置身份认证;

<2>,创建包含@Order的WebSecurityConfigurerAdapter实例,以指定首先使用哪个WebSecurityConfigurerAdapter。

<3>,http.antMatcher()说明此HttpSecurity仅适用于以/api/开头的URL。

<4>,创建WebSecurityConfigurerAdapter的另一个实例,如果URL不以/api/开头,则会使用此配置。这个配置会在ApiWebSecurityConfigurationAdapter之后执行,因为Api..Adapter用@Order(1)注解,而没有@Order注解的默认最后执行。

3.9、 方法安全性

 

从2.0版本开始,Spring Security已经大大改善了对服务层方法的安全性支持。它既提供了框架级的@Secured注解,也支持对JSR-250安全性的注解。从3.0开始,还可以使用新的基于表达式的注解。对单个bean应用安全性时,需要使用拦截方法的元素来修饰bean声明,也可以使用AspectJ样式的切入点在整个服务层中横切多个bean。

        1. EnableGlobalMethodSecurity

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

@EnableGlobalMethodSecurity(securedEnabled = true)

public class MethodSecurityConfig {

// ...

}

然后,向方法添加该注解将限制对方法的访问。Spring Security的原生注解支持为该方法定义一组属性。这些属性将被传递给AccessDecisionManager,以供它作出实际处理:

public interface BankService {

 

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")

public Account readAccount(Long id);

 

@Secured("IS_AUTHENTICATED_ANONYMOUSLY")

public Account[] findAccounts();

 

@Secured("ROLE_TELLER")

public Account post(Account account, double amount);

}

可以使用支持JSR-250的注解。

@EnableGlobalMethodSecurity(jsr250Enabled = true)

public class MethodSecurityConfig {

// ...

}

这些都是基于JSR标准的,允许应用简单的基于角色的约束,但是没有Spring Security的原生注解强大。要使用新的基于表达式的语法,可以象下面这样。

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class MethodSecurityConfig {

// ...

}

等价的Java代码。

public interface BankService {

 

@PreAuthorize("isAnonymous()")

public Account readAccount(Long id);

 

@PreAuthorize("isAnonymous()")

public Account[] findAccounts();

 

@PreAuthorize("hasAuthority('ROLE_TELLER')")

public Account post(Account account, double amount);

}

        1. GlobalMethodSecurityConfiguration

有时可能需要执行比@EnableGlobalMethodSecurity注解所允许的更复杂的操作。对于这些实例来说,可以通过继承GlobalMethodSecurityConfiguration,来确保@EnableGlobalMethodSecurity注解能用于子类。例如,如果想提供一个自定义MethodSecurityExpressionHandler,则可以使用以下配置:

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override

    protected MethodSecurityExpressionHandler createExpressionHandler() {

        // ... create and return custom MethodSecurityExpressionHandler ...

        return expressionHandler;

    }

}

3.10、 后处理配置的对象

 

Spring Security的Java代码不会公开它配置的每个对象的所有属性。这简化了大多数用户的配置操作。毕竟,如果每个属性都暴露出来,用户就可以使用标准的bean配置。

尽管有很好的理由不会直接暴露所有属性,但用户可能仍然需要更高级的配置选项。为了解决这个问题,Spring Security引入了ObjectPostProcessor的概念,它可以用来修改或替换由Java代码创建的许多对象实例。例如,如果想在FilterSecurityInterceptor上配置filterSecurityPublishAuthorizationSuccess属性,则可以:

@Override

protected void configure(HttpSecurity http) throws Exception {

    http

        .authorizeRequests()

            .anyRequest().authenticated()

            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

                public <O extends FilterSecurityInterceptor> O postProcess(

                        O fsi) {

                    fsi.setPublishAuthorizationSuccess(true);

                    return fsi;

                }

            });

}

3.11、 自定义DSL

 

可以在Spring Security中提供定制DSL。例如:

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {

    private boolean flag;

 

    @Override

    public void init(H http) throws Exception {

        // any method that adds another configurer

        // must be done in the init method

        http.csrf().disable();

    }

 

    @Override

    public void configure(H http) throws Exception {

        ApplicationContext context = http.getSharedObject(ApplicationContext.class);

 

        // here we lookup from the ApplicationContext. You can also just create a new instance.

        MyFilter myFilter = context.getBean(MyFilter.class);

        myFilter.setFlag(flag);

        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);

    }

 

    public MyCustomDsl flag(boolean value) {

        this.flag = value;

        return this;

    }

 

    public static MyCustomDsl customDsl() {

        return new MyCustomDsl();

    }

}

注意:这实际上是如何实现像HttpSecurity.authorizeRequests()这样的方法。

自定义DSL可以这样使用:

@EnableWebSecurity

public class Config extends WebSecurityConfigurerAdapter {

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http

            .apply(customDsl())

                .flag(true)

                .and()

            ...;

    }

}

代码按以下顺序执行:

1、Config的configure方法被调用;

2、MyCustomDsl的init方法被调用;

3、MyCustomDsl的configure方法被调用。

如果需要,可以让WebSecurityConfiguerAdapter通过使用SpringFactories来默认添加MyCustomDsl。例如,在类路径上创建一个名为META-INF/spring.factories的文件,内容如下:

org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl

希望禁用默认设置的用户可以明确地这样做。

@EnableWebSecurity

public class Config extends WebSecurityConfigurerAdapter {

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http

            .apply(customDsl()).disable()

            ...;

    }

}

 

4、命名空间配置(xml)

4.1、介绍

命名空间配置自Spring Framework 2.0就开始使用。它允许使用来自其它XML Schema的元素来补充传统的Spring bean应用程序上下文语法。命名空间元素可以简单地用于配置单个Bean,或者定义一种与问题域更接近的配置语法,并向用户隐藏潜在的复杂性。一个简单的元素背后可能会有多个bean配置和处理步骤。例如,将以下元素从安全性命名空间添加到应用程序上下文将启动一个嵌入式LDAP服务器,以在应用程序中测试使用情况:

<security:ldap-server />

这比连接相应的Apache Directory Server bean简单得多。最常见的备选配置需求由ldap-server元素上的属性支持,用户不必担心需要创建哪些bean以及那些bean的属性名称是什么。

要开始在应用程序上下文中使用安全性命名空间,需要在类路径中包含spring-security-config.jar。然后,就是将模式声明添加到应用程序上下文文件中:

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:security="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/security

        http://www.springframework.org/schema/security/spring-security.xsd">

    ...

</beans>

在示例中,会将"security"用作默认命名空间而不是"beans",这意味着,可以忽略所有安全命名空间元素上的前缀,使内容更易于阅读。如果将应用程序上下文划分为单独的文件,并在其中一个文件中包含大部分安全配置,则可能还需要执行此操作。安全应用程序上下文文件将会像这样开始。

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:beans="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/security

        http://www.springframework.org/schema/security/spring-security.xsd">

    ...

</beans:beans>

 

        1. 命名空间的设计

命名空间旨在抓住框架的最常见用途,并提供简化和简洁的语法来在应用程序中启用它们。该设计基于框架内的大规模依赖关系,可分为以下几个方面:

  1. Web/HTTP Security--最复杂的部分。设置过滤器和相关的服务bean,它们是用于框架的身份认证机制,如保护URL,渲染login和error页面等。
  2. 业务对象(方法)安全性--保护服务层的选项。
  3. AuthenticationManager--处理来自框架其它部分的认证请求。
  4. AccessDecisionManager--为web和方法安全提供访问决策。将注册一个默认值,也可以选择使用一个定制值,使用普通的Spring bean语法声明。
  5. AuthenticationProviders--身份验证管理器对用户进行身份验证的机制。命名空间为几个标准选项提供了支持,同时也提供了添加使用传统语法声明的定制bean的方法。
  6. UserDetailsService--与身份认证提供者密切相关,但其它bean通常也需要它。

4.2、 入门

在本节中,将看看如何构建一个命名空间配置,来使用框架的一些主要功能。可将身份认证支持和访问控制添加到现有的Web应用程序,并带有一些测试登录名。然后,研究如何根据数据库或其它安全存储库切换到身份验证。在后面的章节中,将介绍更高级的命名空间配置选项。

        1. web.xml配置

首先是将以下过滤器声明添加到web.xml文件中:

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

 

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

这种配置提供了一个进入Spring Security Web基础设施的钩子。DelegatingFilterProxy是Spring框架的类,它执行一个过滤器实现。在这里,该bean是"springSecurityFilterChain",它是由命名空间创建的用于处理Web安全性的内部bean。Web安全服务使用<http>元素配置。

        1. 最小的<http>配置

要启用web安全性,首先添加以下内容到web.xml。

<http>

  <intercept-url pattern="/**" access="hasRole('USER')" />

  <form-login />

  <logout />

</http>

以上配置说明应用程序中的所有URL都受到保护,只有拥有【ROLE_USER】角色的用户才能访问,要使用带有用户名和密码的表单登录应用程序,并且要注册一个注销的URL,以允许用户正常退出。<http>元素是所有与Web相关的命名空间功能的父元素。&l

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值