Security
Spring Security
Spring Security提供了一套完整的声明式安全访问控制方案。为Spring Boot和Spring Cloud应用提供安全保护相关功能。
核心概念
名称 | 描述 |
Principle | 用户对象 |
Authority | 用户角色 |
Permission | 授权,对角色拥有的权限进行描述 |
在Spring Security中,Principle(用户对象)、Authority(用户角色)、Permission(授权)都是独立的概念,三者之间没有必然联系(数据库映射关系),需要通过配置信息来定义三者之间的关系。
认证与授权
安全方面的实现方案通常由认证(Authentication)和授权(Authorization)两个部分组成。
认证(Authentication)
建立系统使用者信息(Principai)的过程。要求系统用户提供用户名和密码,通过校验用户名和密码的正确性来完成认证成功或认证失败。
Spring Security进行认证的步骤如下:
系统用户通过登录表单输入用户名和密码信息,提交登录表单进行登录请求。
UsernamePasswordAuthenticationFilter获取到表单输入的用户名和密码信息,封装为待认证对象(Authentication)。
认证管理对象AuthenticationManager对未进行认证操作的对象通过AuthenticationProvider接口执行认证操作,AuthenticationProvider提供了多种登录认证方式。
在AuthenticationProvider提供的多种登录认证方式中,使用UserDetailService接口实现类的loadUserByUsername方法执行具体的登录认证操作,在该方法中我们可以自己实现按照登录名和密码信息校验,返回校验通过的登录用户详情(UserDetails)。
将校验通过的登陆用户信息设置到安全上下文(SecurityContext)。
授权(Authorization)
不同用户具有的系统使用权限不同,系统会为不同的用户分配不同的角色,每个系统角色具备一系列的系统权限。授权系统会判断某个用户是否在微服务系统中具备调用服务实例的权限。
在微服务系统实践过程中,最好将认证/授权系统单另抽取成独立的服务实例。
Spring Cloud Security
用于构建微服务的安全应用程序和服务,可以轻松实现微服务系统架构下的统一安全认证与授权。包含以下组件
组件名 | 组件描述 |
spring-cloud-security | 为Zuul、Feign、OAuth2的资源服务器的token提供相关支持 |
spring-cloud-starter-security | 管理在Spring Cloud中使用Spring Security的依赖 |
spring-cloud-starter-oauth2 | 管理在Spring Cloud中使用Spring Security OAuth2依赖 |
OAuth2.0
OAuth2.0是一个标准的授权协议。服务于用户资源和第三方应用之间,使得第三方应用无法直接访问用户资源,需要访问用户资源需要通过提供凭证获得OAuth2.0授权, 从而起到保护用户资源的作用。
如果资源所有者允许第三方应用访问资源,除了将用户名和密码告知第三方应用,让第三方应用以资源所有者身份访问资源,还可以通过授权码的方式让第三方应用对资源进行访问。
OAuth2.0角色
角色名 | 角色描述 |
授权服务提供方(Authorization Server) | 认证服务器,进行访问的认证和授权 |
资源所有者(Resource Owner) | 系统用户 |
资源服务器(Resource Server) | 存放用户资源 |
客户端(Client) | 第三方应用 |
OAuth2.0运行流程
系统用户打开客户端,请求资源拥有者给予授权。
资源拥有者同意给客户端授权。
客户端使用上一个步骤获取到的授权,向认证服务器申请令牌。
认证 服务器对授权信息进行认证,认证通过发放令牌。
客户端使用申请到的令牌向资源服务器申请获取资源。
资源服务器确认令牌无误,向客户端开放资源。
客户端授权模式
简化模式
客户端不通过第三方应用服务器,直接在浏览器中向认证服务器申请令牌。所有步骤在浏览器中完成,令牌对访问者可见,且客户端不需要认证。执行流程如下:
客户端通过浏览器将用户导向认证服务器。包含如下参数:
参数名 参数描述 response_type 授权类型(必填项),此处固定值为"token" client_id 客户端标识(必填项) redirect_uri 重定向URI(选填项) scope 授权范围(选填项) state 客户端状态(必填项,否则存在CSRF漏洞),认证服务器将用户代理重定向时需携带该参数 用户决定是否授权客户端。
如果用户给予授权,认证服务器将用户导向客户端的"重定向URI",并在该URI的Hash参数值部分包含访问令牌。认证服务器响应给客户端的URI中包括以下参数:
参数名 参数描述 access_token 访问令牌(必填项) token_type 令牌类型(必填项)大小写不敏感 expires_in 令牌过期时间(秒),省略该参数需使用其他方式设置过期时间 scope 权限范围,如果与客户端申请时的范围一致可省略该参数 state 客户端状态,认证服务器响应与客户端请求时参数内容一致 客户端通过浏览器向资源服务器发送请求。
资源服务器返回一个网页,其中包括第三步收到的Hash值(参数及对应内容)。
浏览器执行上一步网页中的脚本文件,提取出Hash值相关的令牌内容。
浏览器将获取到的令牌内容传递给客户端。
密码模式
用户向客户端提供用户名和密码信息,客户端使用用户名和密码信息向认证服务器申请授权令牌并返回给用户,用户使用授权令牌访问用户资源。
系统用户访问客户端,需要提供用户名和密码信息。
客户端将用户名和密码信息发送至认证服务器,申请令牌。需要包括如下参数
参数名 参数描述 grant_type 授权类型(必填项),此处固定值为"password" username 用户名(必填项) password 密码(必填项) scope 权限范围(选填项) 认证服务器确认无误,向客户端提供访问令牌。
系统用户通过客户端携带令牌信息访问资源服务器获取用户资源。
客户端模式
客户端以自己的名义向认证服务器进行认证,不存在授权问题。
客户端向认证服务器进行身份验证,并申请一个访问令牌。包含参数如下
参数名 参数描述 grant_type 授权类型(必填项),此处固定值为"clientcredentials" scope 权限范围(选填项) 认证服务器确认无误后向客户端提供访问令牌。
授权码模式
客户端通过后台服务器与认证服务器交互。
用户访问客户端。
客户端利用重定向将用户导向认证服务器申请认证。包含如下参数:
参数名 参数描述 response_type 授权类型(必填项),此处固定值为"code" client_id 客户端标识(必填项) redirect_uri 重定向URI(选填项) scope 申请的授权范围(选填项) state 客户端状态,可以使用任意内容作为参数值(建议动态内容,否则存在CSRF漏洞),认证服务器会返回请求参数原值 用户选择是否给客户端授权。
如果用户同意授权,认证服务器会将用户导向至步骤2中设置的重定向URI参数地址,并附带一个授权码。认证服务器响应给客户端的URI中包含如下参数:
参数名 参数描述 code 授权码(必填项)。有效期很短(几分钟),客户端只能使用该授权码一次,否则会被认证服务器拒绝访问。该授权码与步骤2中的客户端标识和重定向URI为对应关系 state 如果步骤2中包含该参数,认证服务器响应信息也包含该参数且参数值一致 客户端收到授权码,附上"重定向URI",向认证服务器申请令牌。包含如下参数:
参数名 参数描述 grant_type 授权类型(必填项),此处固定值为"authorization_code" code 步骤4中获取到的授权码(必填项) redirect_uri 重定向URI(必填项),参数值与步骤2中重定向URI参数值一致 client_id 客户端ID(必填项) 认证服务器核对授权码和重定向URI,确认无误后向客户端发送访问令牌(access token)和更新令牌(refresh token)。认证服务器的响应内容中包含如下参数:
参数名 参数描述 access_token 访问令牌(必填项) token_type 令牌类型(必填项,参数值内容bearer或mac) expires_in 过期时间(秒) refresh_token 刷新令牌(必填项) scope 授权范围。如果该范围与客户端申请范围一致可省略参数值 如果在用户访问时客户端的访问令牌已经过期,用户需要用更新令牌重新申请一个访问令牌。客户端发出更新令牌的请求参数如下:
参数名 参数描述 grant_type 授权类型(必填项),此处固定值为"authorization_code" refresh_token 刷新令牌(必填项),参数值与步骤6中refresh_token值一致 scope 申请的授权范围(必填项),不得超过步骤6中的授权范围参数值,省略该参数值默认与步骤6授权范围参数值一致
授权模式总结
授权模式 | 使用场景 | 令牌刷新 | 应用范围 |
简化模式 | WEB浏览器 | 不支持 | APP |
密码模式 | 遗留系统 | 支持 | 受信任系统 |
授权码模式 | 第三方应用 | 支持 | 服务端应用之间 |
客户端模式 | 接口调用 | 不支持 | API访问 |
Spring Cloud Security实现OAuth2
OAuth2.0服务提供端
在OAuth2.0中,服务提供端分为"授权服务"和"资源服务"两个角色,通过管理和验证OAuth2.0令牌对资源进行保护和服务,负责将受保护的资源暴露出去。
Spring Security OAuth2.0将"授权服务"和"资源服务"分别放在多个应用中,可以为一个"授权服务"配置多个"资源服务"。获取令牌(Token)的请求由Spring MVC控制器端点处理,访问受保护资源通过标准的Spring Security请求过滤器处理。
Spring Security过滤器实现OAuth2.0授权服务器必须提供以下两个端点
端点名称 | 端点描述 |
AuthorizationEndpoint | 用于授权服务请求,默认URI为/oauth/authrize |
TokenEndpoint | 用于获取访问令牌(access token)请求,默认URI为/oauth/token |
授权服务器
授权服务器搭建的过程中,需要考虑客户端详细信息及令牌服务的具体实现。
在Spring Cloud Security中,可以通过注解@EnableAuthorizationServer来启用授权服务器。并需要实现以下配置信息
配置 | 配置描述 |
ClientDetailServiceConfigurer | 配置客户端信息 |
AuthorizationServerEndpointsConfigurer | 配置授权token节点和token服务 |
AuthorizationServerSecretConfigurer | 配置token节点安全策略 |
ClientDetailServiceConfigurer
用来配置客户端详情信息,可以基于内存和数据库方式管理客户端配置信息,配置内容如下:
配置项 配置描述 clientId 用来标识客户端(必须配置) secret 客户端安全码(SpringBoot2.0以上必须配置此项) scope 限制客户端的访问范围(为空客户端拥有全部访问范围) authorizedGrantTypes 客户端使用的授权类型 authorities 客户端可以使用的权限(基于Spring Security Authorities) 基于内存存储客户端配置信息
@Configuration
public class ClientDetailServiceConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
/**
* 将客户端配置存储于内存中
* @param configurer 客户端详情配置类
*/
public void clientDetailConfig(ClientDetailsServiceConfigurer configurer) throws Exception
{
//设置客户端配置信息存储于内存中,并设置客户端标识
configurer.inMemory().withClient("clientInMemory")
.secret(passwordEncoder.encode("123456"))//使用BCryptPasswordEncoder对密码进行加密
.authorizedGrantTypes("password")//授权访问模式
.scopes("all")//设置访问范围
.accessTokenValiditySeconds(36000);//token有效时间
}
}
AurhorizationServerEndpointsConfigurer
用来配置授权(Authorization)、令牌(Token)访问端点、令牌服务(Token Service)。默认情况下,AuthorizationServerEndpointsConfigurer开启了所有的验证类型,除了密码类型的验证(只有配制了authenticationManager才会开启)。配置内容如下:
配置项 | 配置描述 |
authenticationManager | 密码认证配置项,配置该项密码认证模式才会开启 |
userDetailService | 获取用户详情信息配置项 |
authorizationCodeService | 验证码服务配置项 |
implicitGrantService | 配置管理implicit验证的状态 |
tokenGranter | 管理token granter配置项 |
除了上述配置信息,还需要配置token的管理策略,目前支持如下策略:
策略名 | 策略描述 |
InMemoryTokenStore | token内容存储于内存中 |
JdbcTokenStore | Token存储于数据库中(需引入spring-jdbc依赖包并配置数据源) |
JWTTokenStore | 采用JWT形式,未做任何存储(JWT本身包含,需引入spring-jwt依赖) |
AuthorizationServerSecretConfigurer
用来配置令牌端点安全约束。主要配置内容如下
配置项 | 配置描述 |
ClientDetailService | 根据客户端标识获取客户端详情信息(loadClientByClientId) |
UserDetailService | 根据用户名获取用户详情信息 |
ClientDetailsUserDetailsService | 实现UserDetailsService接口, 持有ClientDetailsService接口属性,获取客户端详情时重写loadClientByClientId方法,将用户名作为参数 |
ClientCredentialsTokenEndpointFilter | 通过拦截/oauth/token获取到clientId和clientSecret参数进行用户认证校验 |
资源服务器
资源服务器(Resource Server)提供了受OAuth2保护的资源(API接口)。Spring OAuth2提供了实现此保护功能的Spring Security认证过滤器。在加了@Configuration注解的配置类上加上@EnableResourceServer注解,启用资源服务器相关功能。
OAuth2Client
用于访问被OAuth2保护起来的资源。客户端需要提供用于存储用户的授权码和访问令牌的机制,需要配置以下内容:
配置项 | 配置描述 |
Protected Resource Configuration | 受保护资源配置 |
Client Configuration | 客户端配置 |
受保护资源配置
使用OAuth2ProtectedResourceDetails类型的Bean来定义受保护的资源,具备如下属性:
属性名 | 属性描述 |
id | 受保护资源标识,无需配置,保持默认内容 |
clientId | OAuth2 Client标识(与授权服务器配置的客户端详情信息中的clientId内容对应) |
clientSecret | 客户端密码(与授权服务器配置的客户端详情信息中的secret内容对应) |
accessTokenUri | 获取Token的API节点 |
scope | 客户端访问范围 |
clientAuthenticationScheme | 客户端验证类型,分别为Http Basic和Form,默认Http Basic |
userAuthorizationUri | 如果用户需要授权访问资源,用户将被重定向到认证的UrI |
客户端配置
使用@EnableOAuth2Client注解简化配置,需要配置以下内容:
配置项 | 配置描述 |
oauth2ClientContextFilter | 存储当前请求和上下文请求 |
AccessTokenRequest | 获取授权令牌请求Bean |