前几节简单介绍了OAuth,以及spring secuirty OAuth的一些基本构成。文中有一些关联知识点的引用,若是要深刻研究,能够点进去详细了解一下,这样能够加深当即。若是你只想快速浏览OAuth server的搭建能够跳过前面几节,直接看下面的代码实例。html
OAuth(开放受权)是一个开放标准,容许用户受权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不须要将用户名和密码提供给第三方移动应用或分享他们数据的全部内容。
Oauth2 受权方式java
关于OAuth不一样受权的具体流程能够参考[理解OAuth 2.0
-阮一峰](http://www.ruanyifeng.com/blo...。算法
spring security OAuth 是对OAuth2协议的一个实现。是在spring security的基础上发展而来,以前是spring security的一个子项目,如今已经独立出来。详细见官网.
spring OAuth使用相似spring secuirty的机制来实现OAuth2 .根据OAuth协议规定,一个完整的受权服务器应当包含:受权服务器和资源服务器。受权服务器负责认证和受权,资源服务器负责根据用户提供的受权凭证(好比给以token)。这里有一篇官方文档介绍了spring OAuth的使用.[OAuth 2 Developers Guide
](http://projects.spring.io/spr...spring
一个受权服务大体几个模块:client管理、受权接口、用户认证、令牌管理。
spring security OAuth授端口数据库
受权是使用 AuthorizationEndpoint 这个端点来进行控制的,你可以使用 AuthorizationServerEndpointsConfigurer 这个对象的实例来进行配置 ,若是你不进行设置的话,默认是除了资源全部者密码(password)受权类型之外,支持其他全部标准受权类型的(RFC6749),咱们来看一下这个配置对象有哪些属性能够设置吧,以下列表:后端
令牌主要有两种解决方案:使用随机算法生成惟一标示与用户受权关联,而后保存起来供校验时查询,为了方便资源服务方便验证令牌,这种方案经常是受权服务和资源服务共存的,若是不共存,那么资源服务的tokenStore服务于受权服务tokenStore要作到数据互通,spring 的解决方案是提供/oauth/check_token接口来完成。第二种是受权服务器使用某种算法生成字符串,资源服务器使用约定好的算法对令牌进行解析校验,以验证 他的合法性。在spring security OAuth2中提供了InMemoryTokenStore、JdbcTokenStore、JwtTokenStore。前面二者须要将令牌存在起来,最后一个JwtTokenStore是jwt令牌TokenStore的实现,他不存储令牌,只根据必定的规则和秘钥验证令牌的合法性。jwt令牌分为三段:头部信息、playload、签名。每段使用base64编码,每段之间使用"."链接。api
一个资源服务(能够和受权服务在同一个应用中,固然也能够分离开成为两个不一样的应用程序)提供一些受token令牌保护的资源,Spring OAuth提供者是经过Spring Security authentication filter来验证过滤器来实现的保护(OAuth2AuthenticationProcessingFilter),你能够经过 @EnableResourceServer 注解到一个 @Configuration 配置类上来标记应用是一个资源服务器,你能够经过配置 ResourceServerConfigurer 配置对象来进行资源服务器的一些自定义配置(能够选择继承自 ResourceServerConfigurerAdapter 而后覆写其中的方法,参数就是这个对象的实例),下面是一些能够配置的属性:缓存
@EnableResourceServer 注解自动增长了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链。
ResourceServerTokenServices 是组成受权服务的另外一半,若是你的受权服务和资源服务在同一个应用程序上的话,你可使用 DefaultTokenServices ,这样的话,你就不用考虑关于实现全部必要的接口的一致性问题,这一般是很困难的。若是你的资源服务器是分离开的,那么你就必需要确保可以有匹配受权服务提供的 ResourceServerTokenServices,它知道如何对令牌进行解码。
在受权服务器上,你一般可使用 DefaultTokenServices 而且选择一些主要的表达式经过 TokenStore(后端存储或者本地编码)。
RemoteTokenServices 能够做为一个替代,它将容许资源服务器经过HTTP请求来解码令牌(也就是受权服务的 /oauth/check_token 端点)。若是你的资源服务没有太大的访问量的话,那么使用RemoteTokenServices 将会很方便(全部受保护的资源请求都将请求一次受权服务用以检验token值),或者你能够经过缓存来保存每个token验证的结果。安全
关于spring OAuth的更多配置细节能够参考官方文档: OAuth2 Autoconfig.spring Secuirty OAuth2配置比较简单,关键仍是要理解OAuth2协议,以及他的使用场景。
<dependencies> <!-- ... other dependency elements ... --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.1.RELEASE</version> </dependency> </dependencies>
受权服务配置主要须要完成如下几个配置:bash
咱们能够继承AuthorizationServerConfigurerAdapter而且覆写其中的三个configure方法来进行配置。下面是简单的
配置demo,配置来源于官方文档。
@Component public class CustomAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter { AuthenticationManager authenticationManager; public CustomAuthorizationServerConfigurer( AuthenticationConfiguration authenticationConfiguration ) throws Exception { this.authenticationManager = authenticationConfiguration.getAuthenticationManager(); } @Override public void configure( ClientDetailsServiceConfigurer clients ) throws Exception { //下面配置了一个简单的客服端 clients.inMemory()// 使用内存保存客服端信息,建议编写本身的clientService来配置客服端 .withClient("client")//客服端的clientid .authorizedGrantTypes("password","client_credentials","refresh_token")//支持的受权方式 .secret("secret")//该客服端访问时的密码 .scopes("all");//scope范围 } @Override public void configure( AuthorizationServerEndpointsConfigurer endpoints ) throws Exception { endpoints.authenticationManager(authenticationManager); endpoints.tokenStore(tokenStore()); } @Bean public TokenStore tokenStore() { return new InMemoryTokenStore(); } }
以上完成了一个简单的受权服务配置,咱们的配置重点放在了client的配置上,因为咱们为配置资源拥有者(用户),咱们没法使用password受权,应为用户信不存在,咱们不管输入用户名和密码都不正确。所以我暂且只能使用,client_credentials,这个仅仅是client受权,不牵扯到用户信息。使用下面命令
curl client:secret@localhost:8080/oauth/token -d grant_type=client_credentials
一切顺利的话咱们能够获得token相关信息。若是要使用其余好比password等用户受权,那么须要在受权服务器配置用户相关信息,并把相关的userDetailService配置到AuthorizationServerEndpointsConfigurer对象上,上面有提到endponint配置相关规则。
资源服务的做用前面已经介绍过了,资源服务主要关注两个事情,对token进行鉴权,根据具体权限包含资源。因为token多是其余服务器生成,所以要作到ResourceServerTokenServices与受权服务的tokenService相匹配。下面是简单的配置.
@EnableResourceServer public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { // 注意这个TokenStore须要和以前配置的一致 private final TokenStore tokenStore; public ResourceServerConfiguration(TokenStore tokenStore) { this.tokenStore = tokenStore; } // 资源服务器安全规则配置 @Override public void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) .and() .csrf() .disable() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/register").permitAll() .antMatchers("/api/activate").permitAll() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/account/reset-password/init").permitAll() .antMatchers("/api/account/reset-password/finish").permitAll() .antMatchers("/api/**").authenticated() // 配置scope权限访问 // .antMatchers("/api/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))"); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("jhipster-uaa") .tokenStore(tokenStore); } }
上面是资源服务和受权服务在一台机器上时的配置。因为共享tokenStore,所以资源服务在验证token的时候不会出任何问题。
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends ResourceServerConfigurerAdapter { private final OAuth2Properties oAuth2Properties; public SecurityConfiguration(OAuth2Properties oAuth2Properties) { this.oAuth2Properties = oAuth2Properties; } // 主要配置资源的保护规则 @Override public void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/**").authenticated() .antMatchers("/management/health").permitAll() .antMatchers("/management/info").permitAll() .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN); } // 配置tokenstore,资源服务器在进行token验证的时候须要, @Bean public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(OAuth2SignatureVerifierClient signatureVerifierClient) { return new OAuth2JwtAccessTokenConverter(oAuth2Properties, signatureVerifierClient); } }
上面是采用jwt,资源/受权服务分离的配置,以上配置没法和上面的受权服务配置配合使用。若要配置使用,须要修改受权服务的TokenStore为JwtTokenStore,而且在OAuth2JwtAccessTokenConverter中需访问受权服务器/token/access_key获取RSA公钥,该公钥会在token验证时用到。