在项目中各个服务模块产生的接口并不希望被第三方滥用,所以在向外暴露接口的网关服务中增加了拦截器,并对其进行了简单的安全限制。
但在实际需求中,微服务所产生的接口可能会服务于本系统的多个客户端:iOS,Android,web,pc 等,或者将这些接口开放给第三方系统使用。在提供服务的同时需要一套授权机制来保护安全,在网关服务中增加拦截器的做法略简单,为此spring 提供了spring security 框架来解决这一问题,并且实现了oAuth2.0协议对各类客户端进行授权管理。
OAuth2.0 协议介绍:
OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。
OAuth2.0 是OAuth协议的下一版本,但不向后兼容OAuth1.0 。OAuth2.0关注客户端开发者的简易性,同时为web应用,桌面应用,手机提供专门的认证流程,目前主要使用的版本是OAuth2.0 。
协议参与者 |
|
Resource owner |
资源所有者,对资源具有授权能力的人 |
Resource server |
资源服务器,它存储资源,并处理对资源的访问请求
扫描二维码关注公众号,回复:
2652090 查看本文章
|
Client |
第三方应用,它获得RO的授权后便可去访问RO的资源 |
Authorization Server |
授权服务器 |
- 客户端向资源所有者(用户)请求授权
- 客户端收到授权许可
- 客户端于授权服务器进行身份认证并出示授权许可请求访问令牌
- 授权服务器验证客户端身份并验证授权许可,若有效则办法访问令牌
- 客户端从资源服务器请求受保护资源并出示访问令牌进行身份验证
- 资源服务器验证访问令牌,若有效则满足该请求
授权模式:
OAuth2.0定义了四种授权模式。
1.授权码模式:是功能最完整,流程最严密的授权模式。它的特点就是通过客户端的后台服务器,于服务提供商的认证服务器进行互动。
2.简化模式:不通过第三方应用程序的服务器,而是直接在浏览器中向认证服务器申请令牌,跳过了“授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
3.密码模式:用户向客户端提供自己的用户名和密码。客户端使用这些信息,向服务商提供商索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得存储密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
4.客户端模式:指客户端以自己的名义,而不是以用户的名义,向服务提供商进行认证。严格地说,客户端模式并不属于oauth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求服务提供商提供服务,其实不存在授权问题。
在Dubbo 中使用OAuth2.0
dubbo 框架中的网关模块代理了dubbo服务的web调用,并将这些服务转化为HTTP协议的API接口。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring-boot-starter-security
|
Spring安全框架 |
spring-security-oauth2 |
Spring 安全框架的oauth2.0协议扩展 |
spring-boot-starter-data-redis |
引入redis缓存高频调用的access token信息以提高性能 |
application.properties:
spring.redis.host=localhost
spring.redis.database=0
spring.redis.port=6379
spring.security.oauth2.resource.filter-order=6
security.oauth2.resource.filter-order :设置OAuth 框架过滤器的级别。
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll().antMatchers("/public/{id}").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
每个服务提供的URL接口便是资源,通过继承ResourceServerConfigurerAdapter类并重写configure 方法完成对资源的配置,有点类似于过滤器限制所要连接的URL。并通过@EnableResourceServer注解表示该应用为资源服务。
HttpSecurity 是 securityBuilder 接口的实现类,用于构建HTTP安全相关的配置,并且可以通过链式编程的方式完成自定义的配置。
authorizeRequests |
注册url 规则权限匹配 |
antMatchers |
匹配请求 |
permitAll |
不进行权限拦截 |
anyRequest |
任何请求 |
authenticated |
进行认证 |
httpBasic |
开启HTTP基础认证 |
and |
连接符 |
通过继承AuthorizationServerConfigurerAdapter配置授权服务策略,并使用@EnableAuthorizationServer注解表示该应用是授权服务应用。
ClientDetailsServiceConfigurer配置客户端授权管理信息,如果拥有多个客户端(开放平台系统),可以根据提供的clientId从数据库中查询,并将各相关配置提交给BaseClientDetails。
setClientId |
客户端ID ,必要 |
SetClientSecret |
客户端密码 |
SetScope |
权限范围 |
setAccessTokenValiditySeconds
|
Token创建时的超时时间 |
SetRefershTokenValiditySeconds |
Token刷新时间 |
SetAuthorities |
客户端拥有的身份 |
SetAuthorizedGrantTypes |
客户端拥有的授权方式 |
Password |
密码模式 |
Client_credentials |
客户端模式 |
Refresh_token |
刷新token |
Implicit |
简化模式 |
AuthorizationServerEndpointsConfigurer
|
用于配置授权管理器策略于存储token的策略,通过注入token Store指定将token信息存储在redis中 |
EnableGlobalMethodSecurity
|
设置全局方法安全控制,通过prepostEnabled =true 设置在方法调用之前拦截,并进行权限验证 |
WebSecurityConfigurerAdapter
|
来自于spring web 安全管理框架,用于对访问web用户身份进行安全配置,需要配合@EnableWebSecurity注解开启安全验证 |
UserDetailsService
|
|
User |
接口实现类UserDetails()用于封装用户名,密码,角色等信息,并且需要按照格式ROLE_*指定用户角色 |
FilterRegistrationBean
|
|
PasswordEncoder |
一般情况下数据库中存储的用户密码会经过加密处理,但是用户申请access_token提交的则是明文密码,如果不指定校验密码时的加密策略肯定无法通过验证。这里通过注入passwordEncoder来告知spring security 使用bcrypt 策略进行加密。 同样,对应创建用户存储密码时可以使用new BcryptPasswordEncoder().encode(password)对密码进行加密处理 |
preAuthorize |
对应@EnableGlobalMethodSecurity注解,在该方法调用之前进行拦截,并通过指定的spring EL表达式验证权限。该注解可以应用在任意spring管理的方法之上。 |
hasRole('ROLE_USER')
|
限定用户角色为ROLE_USER访问 |
Or,and |
连接符 |
#oauth2.clientHasRole('ROLE_CLIENT')
|
限定客户端角色为ROLE_CLIENT访问 |
hasIpAddress(192.168.0/24) |
限定IP地址为192.168.0.24的IP访问 |
HasAuthority(‘user’) |
限定名为user的用户访问 |
Principal |
在访问受保护资源时,需要提交access_token完成身份验证,通过在形参中注入principal便可获得当前访问的access_token所对应的用户名称 |
TokenStore |
当用户修改了用户名或密码后,会涉及到重新生成access_token的需求。之前在配置spring security 的权限管理策略时通过注入token store 指定使用redis存储token 信息,同样也可以使用tokenstore找到token并删除。 |
部署应用后访问http://localhost:8080//public/1 成功返回结果,但是当访问/private/1时拦截器生效,提示需要授权才可以访问呢。
之前设置过web和app两个客户端,可以通过post 方式获取,但oauth2 协议中客户端信息需要使用basic auth方式传递,为了方便测试,可以使用chrome 浏览器的postman插件。