这篇教程是展示如何设置一个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. <authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
  6. <authentication-provider user-service-ref="client-details-user-service"/>
  7. </authentication-manager>
  8. <bean id="client-details-user-service" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
  9. <constructor-arg ref="client-details-service" />
  10. </bean>

配置用户授权服务

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

  1. <bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>
  2. <sec:authentication-manager alias="userAuthenticationManager">
  3. <sec:authentication-provider user-service-ref="userService">
  4. <sec:password-encoder ref="passwordEncoder"/>
  5. </sec:authentication-provider>
  6. </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. @RolesAllowed({"ROLE_USER"})
  7. @GET
  8. public ApiUser getUser(final @Context SecurityContext securityContext) {
  9. User requestingUser = loadUserFromSecurityContext(securityContext);
  10. if(requestingUser == null) {
  11. throw new UserNotFoundException();
  12. }
  13. return new ApiUser(requestingUser);
  14. }
  15. protected User loadUserFromSecurityContext(SecurityContext securityContext) {
  16. OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();
  17. Object principal = requestingUser.getUserAuthentication().getPrincipal();
  18. User user = null;
  19. if(principal instanceof User) {
  20. user = (User)principal;
  21. } else {
  22. user = userRepository.findByEmailAddress((String)principal);
  23. }
  24. return user;
  25. }
  26. }

测试这个应用,启动:

> ./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

使用Spring Security和OAuth2实现RESTful服务安全认证的更多相关文章

  1. Spring Security实现OAuth2.0授权服务 - 进阶版

    <Spring Security实现OAuth2.0授权服务 - 基础版>介绍了如何使用Spring Security实现OAuth2.0授权和资源保护,但是使用的都是Spring Sec ...

  2. 使用Spring Security Oauth2完成RESTful服务password认证的过程

            摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...

  3. Spring Security实现OAuth2.0授权服务 - 基础版

    一.OAuth2.0协议 1.OAuth2.0概述 OAuth2.0是一个关于授权的开放网络协议. 该协议在第三方应用与服务提供平台之间设置了一个授权层.第三方应用需要服务资源时,并不是直接使用用户帐 ...

  4. Spring Security 与 OAuth2 介绍

    个人 OAuth2 全部文章 Spring Security 与 OAuth2(介绍):https://www.jianshu.com/p/68f22f9a00ee Spring Security 与 ...

  5. Spring Security 与 OAuth2(介绍)

    https://www.jianshu.com/p/68f22f9a00ee Spring Security 与 OAuth2(介绍) 林塬 2018.01.23 11:14* 字数 3097 阅读 ...

  6. Spring Security基于Oauth2的SSO单点登录怎样做?一个注解搞定

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼.本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring ...

  7. 实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务

    本文为实战SpringCloud响应式微服务系列教程第九章,讲解使用Spring WebFlux构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 从本节开始我们 ...

  8. Spring Security开发安全的REST服务

    第1章 课程导学 项目介绍 Java实战:Spring Security开发安全的REST服务,来自慕客网的视频 ,主要讲认证和授权. 企业级的认证和授权 从0开始实现一个可重用的,企业级的,认证和授 ...

  9. spring oauth2 ,spring security整合oauth2.0 JdbcTokenStore实现 解决url-pattern .do .action

    参考以下两个文章: http://www.cnblogs.com/0201zcr/p/5328847.html http://wwwcomy.iteye.com/blog/2230265 web.xm ...

随机推荐

  1. JAVA 9 新特性

     Oracle已将JAVA 9的开发提上日程.OpenJDK上已经出现了关于下一个主版本JAVA 9的改进建议(JEP).与以往不同,Oracle在这次谈及了一些真正的特性.而早期对于JDK9的声明仅 ...

  2. python 列表的浅拷贝和深拷贝

    转自:https://www.cnblogs.com/laolibk/p/7821369.html 浅拷贝 shallow copy 和深拷贝 deep copy list.copy() 浅拷贝:复制 ...

  3. android activity 窗口 样式

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 将 活动 设置成 窗口样式, 可以设置 主题 为 对话框, 或者 半透明. 安卓:主题= ...

  4. [BZOJ5287][HNOI2018]毒瘤(虚树DP)

    暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...

  5. c++ 时间相关的类型

    关于时间转换可以参考以下博客: https://www.jianshu.com/p/80de04b41c31 https://www.cnblogs.com/qicosmos/p/3642712.ht ...

  6. gzez某蒟蒻lyy的博客

    在gz,想去sn幻想乡也行,现在高一并且是已经高二但仍然是机房最弱,没救了 愿诸位身体健康 水平不行,写出来的东西很sb,但还是会偶尔记录一下... 数学公式测试:$\binom n{n_1\cdot ...

  7. [CodeVS1243]网络提速

    题目大意: 有n个点的连通图,有m次可以将某一条边权值减半的机会. 不同的机会可以叠加作用于同一条边. 求1~n的最短路. 思路: 拆点,记录1到每个点在使用不同次数的机会后的最短路,然后直接跑Dij ...

  8. OpenCV/CUDA/Qt 环境配置小结

    OpenCV Qt CUDA windows环境下 配置 反复装过几次,每次都网搜攻略:自个做个记录 方便以后使用. 碰到OpenCV各种奇怪的错误 先看看 图片imread() 有没有读空 再找其他 ...

  9. 3D数学读书笔记——四元数

    本系列文章由birdlove1987编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhurui_idea/article/details/25400659 什么是四元数 ...

  10. MVC借助Masonry实现图文瀑布流

    借助Masonry可轻松实现瀑布流.本篇实现一个简单的图文瀑布流效果,如下: 图文瀑布流显示的2个要素是图片路径和文字内容,对应的Model为: namespace MvcApplication1.M ...