专注人工智能在金融领域的应用

spring-security-oauth2实例讲解

现在很多网站都有对应的移动版本,那么移动端访问服务端的服务怎么控制权限(比如:如何给Restfull api 增加访问权限),个人所了解主要有两种方法:

  1. 模拟浏览器,访问服务的时候会生成session,之后在移动端缓存cookie,每次网络请求都把cookie加上,
  2. 通过oauth2,登录之后生成一个凭证,每次请求时携带凭证,当然oauth2更多的是为第三方应用提供访问自己服务的权限。

oauth2的配置,可以纯配置文件打造,相比较前面的那些,可以说是最简单也是最复杂的,简单是因为引入jar包配置一个xml就可以,复杂是说这个仅有的xml需要写的东西很多理解起来也要费劲。

1. pom.xml

首先导入oauth2的包:

  1. <properties>  ……  
  2.   <spring-security-oauth2.version>2.0.2.RELEASE</spring-security-oauth2.version>
  3. </properties>  
  4.   
  5. <dependencies>  ……  
  6.   <dependency>   
  7.  <groupId>org.springframework.security.oauth</groupId>    
  8. <artifactId>spring-security-oauth2</artifactId>   
  9.  <version>${spring-security-oauth2.version}</version>  
  10. </dependency>  ……  
  11. </dependencies>  

 

2. applicationContext-security.xml

oauth2是security的一部分,配置也有关联,就不再单建文件,首先要在最前面加一些schema:

  1. <beans:beans xmlns=“http://www.springframework.org/schema/security”  
  2.     xmlns:beans=“http://www.springframework.org/schema/beans”   
  3.     xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”  
  4.     xmlns:oauth2=“http://www.springframework.org/schema/security/oauth2″  
  5.     xsi:schemaLocation=”http://www.springframework.org/schema/beans  
  6.       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
  7.       http://www.springframework.org/schema/security/oauth2  
  8.       http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd      http://www.springframework.org/schema/security  
  9.       http://www.springframework.org/schema/security/spring-security-3.2.xsd”>  

其实就是加了一个oauth2的命名。

在这个文件的开始,也就是之前配置的拦截链的http标签前面并列一个http标签:

  1. <http pattern=“/oauth/token” create-session=“stateless” authentication-manager-ref=“oauth2AuthenticationManager”    
  2.   entry-point-ref=“oauth2AuthenticationEntryPoint”>    
  3.     <intercept-url pattern=“/oauth/token” access=“IS_AUTHENTICATED_FULLY”/>    
  4.     <anonymous enabled=“false”/>    
  5.     <http-basic entry-point-ref=“oauth2AuthenticationEntryPoint”/>    
  6.     <custom-filter ref=“clientCredentialsTokenEndpointFilter” before=“BASIC_AUTH_FILTER”/>    
  7.     <access-denied-handler ref=“oauth2AccessDeniedHandler”/>    
  8. </http>  

这个标签处理/oauth/token的网络请求,这是oauth2的登录验证请求,那么登录需要什么,首先,和Spring Security一样,需要一个认证管理器,Spring Oauth2需要两个认证管理器,第一个就是之前Spring中配置的那一个,用来验证用户名密码的,还有一个是用来区分客户端用户的,给它起个名字叫oauth2AuthenticationManager:

  1. <oauth2:client-details-service id=“clientDetailsService”>  
  2.     <oauth2:client client-id=“mobile_1″ authorized-grant-types=“password,authorization_code,refresh_token,implicit”  
  3.                 secret=”secret_1″scope=”read,write,trust” />
  4. </oauth2:client-details-service>
  5. <beans:bean id=“oauth2ClientDetailsUserService”  
  6.                 class=“org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService”>  
  7.     <beans:constructor-arg ref=“clientDetailsService”/>
  8. </beans:bean>
  9. <authentication-manager id=“oauth2AuthenticationManager”>  
  10.  <authentication-provider user-service-ref=“oauth2ClientDetailsUserService”/></authentication-manager>  

这儿设置了一种客户端,id叫做mobile_1,secret叫做secret_1,针对read、write和trust几个域有效。这几个域会在访问控制中被用到。

当登录成功之后会得到一个token,再次访问的时候需要携带这个token,spring-oauth2根据这个token来做认证,那么spring-oauth2必须先存一份token和用户关系的对应,因为不用session了,这就相当于session,那么这个token在服务器中怎么存,有两种主要的存储方式,一是创建数据表,把token存到数据库里,我现在追求简单可用,采用第二种方式,直接存到内存里。下面配置一个管理token的service:

  1. <beans:bean id=“tokenStore” class=“org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore”/><beans:bean id=“tokenServices” class=“org.springframework.security.oauth2.provider.token.DefaultTokenServices”>    
  2.     <beans:property name=“tokenStore” ref=“tokenStore”/>    
  3.     <beans:property name=“supportRefreshToken” value=“true”/></beans:bean>  

下面配置4个基本的bean:分别处理访问成功、访问拒绝、认证点和访问控制:

  1. <beans:bean id=“oauth2AuthenticationEntryPoint”        class=“org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint”/>  
  2. <beans:bean id=“oauth2AccessDeniedHandler”        class=“org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler”/>
  3. <beans:bean id=“oauthUserApprovalHandler”   
  4.         class=“org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler”/>
  5. <beans:bean id=“oauth2AccessDecisionManager” class=“org.springframework.security.access.vote.UnanimousBased”>  
  6. <beans:constructor-arg>    
  7. <beans:list>      
  8. <beans:bean class=“org.springframework.security.oauth2.provider.vote.ScopeVoter”/>      
  9. <beans:bean class=“org.springframework.security.access.vote.RoleVoter”/>      
  10. <beans:bean class=“org.springframework.security.access.vote.AuthenticatedVoter”/>    
  11. </beans:list>  
  12. </beans:constructor-arg>
  13. </beans:bean>  

下一步,配置这个oauth2的server所能支持的请求类型:

  1. <oauth2:authorization-server client-details-service-ref=“clientDetailsService” token-services-ref=“tokenServices”  user-approval-handler-ref=“oauthUserApprovalHandler” >    
  2.     <oauth2:authorization-code />  
  3.     <oauth2:implicit />  
  4.     <oauth2:refresh-token />  
  5.     <oauth2:client-credentials />  
  6.     <oauth2:password /></oauth2:authorization-server>  

比如说,如果配置本服务器不支持刷新token,那么就:

  1. <oauth2:refresh-token disabled=“true” />  

我们的请求里,要把验证类型、用户名密码都作为表单参数提交,这就需要配置下面的filter:

  1. <beans:bean id=“clientCredentialsTokenEndpointFilter”  
  2.                 class=“org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter”>  
  3.     <beans:property name=“authenticationManager” ref=“oauth2AuthenticationManager”/></beans:bean>  

下面定义一种资源,指定spring要保护的资源,如果没有这个,访问控制的时候会说没有Authentication object:

  1. <oauth2:resource-server id=“mobileResourceServer”  
  2.         resource-id=“mobile-resource” token-services-ref=“tokenServices” />  

好了,到此为止基本配置就都有了,下面就看访问控制的配置:在前面的拦截链上,已经为登录验证配了一个/auth/token,在这个标签下面添加对/json和/admin这两个路径的控制:

  1. <http pattern=“/json**” create-session=“never”  entry-point-ref=“oauth2AuthenticationEntryPoint”  access-decision-manager-ref=“oauth2AccessDecisionManager”>  
  2.     <anonymous enabled=“false” />  
  3.     <intercept-url pattern=“/json**” access=“ROLE_USER” />  
  4.     <custom-filter ref=“mobileResourceServer” before=“PRE_AUTH_FILTER” />  
  5.     <access-denied-handler ref=“oauth2AccessDeniedHandler” /></http><http pattern=“/admin**” create-session=“never”  entry-point-ref=“oauth2AuthenticationEntryPoint”  access-decision-manager-ref=“oauth2AccessDecisionManager”>  
  6.     <anonymous enabled=“false” />  
  7.     <intercept-url pattern=“/admin**” access=“ROLE_ADMIN,SCOPE_READ” />  
  8.     <custom-filter ref=“mobileResourceServer” before=“PRE_AUTH_FILTER” />  
  9.     <access-denied-handler ref=“oauth2AccessDeniedHandler” /></http>  

我们用oauth2AccessDecisionManager来做决策,这个地方需要注意,spring-security里面配置access=”ROLE_USER,ROLE_ADMIN”是说user和admin都可以访问,是一个“或”的关系,但是这里是“与”的关系,比如第二个,需要ROLE_ADMIN并且当前的scope包含read才可以,否则就没有权限。认证失败会返回一段xml,这个可以自定义handler来修改,暂且按下不表。

http://localhost:8080/demo4ssh-security-oauth2/oauth/token?client_id=mobile_1&client_secret=secret_1&grant_type=password&username=zhangsan&password=123456

这时候会返回一个access_token:

{"access_token":"4219a91f-45d5-4a07-9e8e-3acbadd0c23e","token_type":"bearer","refresh_token":"d41df9fd-3d36-4a20-b0b7-1a1883c7439d","expires_in":43199,"scope":"read write trust"}

这之后再拿着这个access_token去访问资源:

http://localhost:8080/demo4ssh-security-oauth2/admin?access_token=4219a91f-45d5-4a07-9e8e-3acbadd0c23e

Demo源码地址:https://github.com/Duttor/oauth.git

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>