使用Spring Security和OAuth2实现RESTful服务安全认证

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

这篇教程是展示如何设置一个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

相关文章
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
2365 5
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
14606 60
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
475 4
|
9月前
|
Prometheus 监控 Cloud Native
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务实现步骤
778 0
|
人工智能 自然语言处理 Java
对话即服务:Spring Boot整合MCP让你的CRUD系统秒变AI助手
本文介绍了如何通过Model Context Protocol (MCP) 协议将传统Spring Boot服务改造为支持AI交互的智能系统。MCP作为“万能适配器”,让AI以统一方式与多种服务和数据源交互,降低开发复杂度。文章以图书管理服务为例,详细说明了引入依赖、配置MCP服务器、改造服务方法(注解方式或函数Bean方式)及接口测试的全流程。最终实现用户通过自然语言查询数据库的功能,展示了MCP在简化AI集成、提升系统易用性方面的价值。未来,“对话即服务”有望成为主流开发范式。
8930 7
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
413 0
|
网络协议 Java Shell
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
966 7
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
263 1
|
Java Spring 安全
Spring 框架邂逅 OAuth2:解锁现代应用安全认证的秘密武器,你准备好迎接变革了吗?
【8月更文挑战第31天】现代化应用的安全性至关重要,OAuth2 作为实现认证和授权的标准协议之一,被广泛采用。Spring 框架通过 Spring Security 提供了强大的 OAuth2 支持,简化了集成过程。本文将通过问答形式详细介绍如何在 Spring 应用中集成 OAuth2,包括 OAuth2 的基本概念、集成步骤及资源服务器保护方法。首先,需要在项目中添加 `spring-security-oauth2-client` 和 `spring-security-oauth2-resource-server` 依赖。
420 0