zoukankan      html  css  js  c++  java
  • 使用Spring Security和OAuth2实现RESTful服务安全认证

    这篇教程是展示如何设置一个OAuth2服务来保护REST资源. 源代码下载github. (https://github.com/iainporter/oauth2-provider)你能下载这个源码就开始编写一个被OAuth方法保护的服务。该源码包含功能:

    * 用户注册和登录
    * Email验证
    * Password 丢失

    采取的技术有以下:

    * OAuth2 Protocol 
    spring Security 
    * Spring Integration 
    * Spring Data 
    * Jersey/JAX-RS 
    * Gradle / Groovy 
    MongoDB

    通过以下方式构建项目:

    Git clone  git@github.com:iainporter/oauth2-provider.git 
    > cd oauth2-provider 
    > ./gradlew clean build integrationTest

    运行Web项目:

    这个应用是基于MongoDB作为持久层,在运行应用之前确认mongod是运行在端口27017.

    运行命令:

    > ./gradlew tomcatRun

    在浏览器打开http://localhost:8080/oauth2-provider/index.html

    1. 创建一个用户:

    curl -v -X POST
       -H "Content-Type: application/json"
       -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM="
       -d '{"user":{"emailAddress":"user@example.com"}, "password":"password"}'
       'http://localhost:8080/oauth2-provider/v1.0/users'

    结果应该是:

    {"apiUser":
       {"emailAddress":"user@example.com",
       "firstName":null,
       "lastName":null,
       "age":null,
       "id":"8a34d009-3558-4c8c-a8da-1ad2b2a393c7",
       "name":"user@example.com"},
       "oauth2AccessToken":
       {"access_token":"7e0e4708-7837-4a7e-9f87-81c6429b02ac",
       "token_type":"bearer", 
       "refresh_token":"d0f248ab-e30f-4a85-860c-bd1e388a39b5",
       "expires_in":5183999,
       "scope":"read write"
       }
    }

    2. 请求一个access token:

    curl -v -X POST
       -H "Content-Type: application/json"
       -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM="
       'http://localhost:8080/oauth2-provider/oauth/token?grant_type=password&username=user@example.com&password=password'

    结果应该是:

    {
      "access_token":"a838780e-35ef-4bd5-92c0-07a45aa74948",
      "token_type":"bearer",
      "refresh_token":"ab06022f-247c-450a-a11e-2ffab116e3dc",
      "expires_in":5183999
    }

    3. 刷新一个token:

    curl -v -X POST
       -H "Content-Type: application/json"
       -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM="
       'http://localhost:8080/oauth2-provider/oauth/token?grant_type=refresh_token&refresh_token=ab06022f-247c-450a-a11e-2ffab116e3dc'

    结果应该是:

    {
       "access_token":"4835cd11-8bb7-4b76-b857-55c6e7f36fc4",
       "token_type":"bearer",
       "refresh_token":"ab06022f-247c-450a-a11e-2ffab116e3dc",
       "expires_in":5183999
    }

    Web Context

    一个Jersey 处理所有资源调用:

    1. <servlet-mapping>  
    2.         <servlet-name>jersey-servlet</servlet-name>  
    3.         <url-pattern>/*</url-pattern>  
    4. </servlet-mapping>  

    Spring servlet处理所有oauth 调用:

    1. <servlet-mapping>  
    2.         <servlet-name>spring</servlet-name>  
    3.         <url-pattern>/oauth/*</url-pattern>  
    4. </servlet-mapping>  

    spring security配合定义一个过滤器:

    1. <filter>  
    2.     <filter-name>springSecurityFilterChain</filter-name>  
    3.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    4.     <init-param>  
    5.         <param-name>contextAttribute</param-name>  
    6.         <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>  
    7.     </init-param>  
    8. </filter>  

    对根目录下所有url进行 过滤:

    1. <filter-mapping>  
    2.     <filter-name>springSecurityFilterChain</filter-name>  
    3.     <url-pattern>/*</url-pattern>  
    4. </filter-mapping>  

    配置OAuth 流程

    1. <oauth:authorization-server client-details-service-ref="client-details-service" token-services-ref="tokenServices">  
    2.         <oauth:refresh-token/>  
    3.         <oauth:password/>  
    4.     </oauth:authorization-server>  

    缺省的token端点是/oauth/token ,只有 password flow 和刷新 token 支持。

    保护token端点

    使用Spring security 保护token端点:

    1. <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"  
    2.       xmlns="http://www.springframework.org/schema/security">  
    3.     <anonymous enabled="false"/>  
    4.     <http-basic entry-point-ref="clientAuthenticationEntryPoint"/>  
    5.     <access-denied-handler ref="oauthAccessDeniedHandler"/>  
    6. </http>  

    下面配置授权authentication 管理器和客户端服务:

    1. <bean id="clientCredentialsTokenEndpointFilter"  
    2.       class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">  
    3.     <property name="authenticationManager" ref="clientAuthenticationManager"/>  
    4. </bean>  
    5.   
    6. <authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">  
    7.     <authentication-provider user-service-ref="client-details-user-service"/>  
    8. </authentication-manager>  
    9.   
    10.   
    11. <bean id="client-details-user-service" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">  
    12.     <constructor-arg ref="client-details-service" />  
    13. </bean>  

    配置用户授权服务

    Resource Owner Password flow 需要管理用户的授权管理器

    1. <bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>  
    2.   
    3. <sec:authentication-manager alias="userAuthenticationManager">  
    4.     <sec:authentication-provider user-service-ref="userService">  
    5.         <sec:password-encoder ref="passwordEncoder"/>  
    6.     </sec:authentication-provider>  
    7. </sec:authentication-manager>  

    密码 password encoder是用于加密密码。用户服务必须实现一个UserDetailsService ,能根据用户名返回用户。

    1. @Override  
    2. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
    3.     notNull(username, "Mandatory argument 'username' missing.");  
    4.     User user = userRepository.findByEmailAddress(username.toLowerCase());  
    5.     if (user == null) {  
    6.         throw new AuthenticationException();  
    7.     }  
    8.     return user;  
    9. }  

    配置Token 服务

    1. <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">  
    2.     <property name="tokenStore" ref="tokenStore"/>  
    3.     <property name="supportRefreshToken" value="true"/>  
    4.     <property name="clientDetailsService" ref="client-details-service"/>  
    5. </bean>  

    保护资源访问

    1. <oauth:resource-server id="resourceServerFilter" token-services-ref="tokenServices"/>  

    核心服务

    这个服务提供基于访问token获得用户的信息。URL格式:

    /v1.0/users/{id}/someresource 

    1. @Path("/v1.0/me")  
    2. @Component  
    3. @Produces({MediaType.APPLICATION_JSON})  
    4. @Consumes({MediaType.APPLICATION_JSON})  
    5. public class MeResource extends BaseResource {  
    6.   
    7.     @RolesAllowed({"ROLE_USER"})  
    8.     @GET  
    9.     public ApiUser getUser(final @Context SecurityContext securityContext) {  
    10.         User requestingUser = loadUserFromSecurityContext(securityContext);  
    11.         if(requestingUser == null) {  
    12.             throw new UserNotFoundException();  
    13.         }  
    14.         return new ApiUser(requestingUser);  
    15.     }  
    16.   
    17.     protected User loadUserFromSecurityContext(SecurityContext securityContext) {  
    18.         OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();  
    19.         Object principal = requestingUser.getUserAuthentication().getPrincipal();  
    20.         User user = null;  
    21.         if(principal instanceof User) {  
    22.             user = (User)principal;  
    23.         } else {  
    24.             user = userRepository.findByEmailAddress((String)principal);  
    25.         }  
    26.         return user;  
    27.     }  
    28. }  

    测试这个应用,启动:

    > ./gradlew tomcatRun

    测试:

    curl -v -X GET
      -H "Content-Type: application/json"
      -H "Authorization: Bearer [your token here]"
      'http://localhost:8080/oauth2-provider/v1.0/me'

    参考:https://github.com/tcompiegne/couchbase-token-store-spring-oauth2

                https://github.com/tcompiegne/oauth2-server-spring-couchbase

    转自:http://www.jdon.com/dl/best/securing-rest-services-with-spring.html.html

  • 相关阅读:
    JS 实现的年月日三级联动
    【代码总结● Swing中的一些操作与设置】
    S7通信协议之你不知道的事儿
    .NetCore程序在Linux上面部署的实现
    什么是哈希表?
    什么是队列?
    什么是栈?
    什么是数组?
    什么是数据结构?
    什么是链表?
  • 原文地址:https://www.cnblogs.com/pangguoming/p/6846222.html
Copyright © 2011-2022 走看看