使用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 处理所有资源调用:
- <servlet-mapping>
- <servlet-name>jersey-servlet</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
Spring servlet处理所有oauth 调用:
- <servlet-mapping>
- <servlet-name>spring</servlet-name>
- <url-pattern>/oauth/*</url-pattern>
- </servlet-mapping>
spring security配合定义一个过滤器:
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- <init-param>
- <param-name>contextAttribute</param-name>
- <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>
- </init-param>
- </filter>
对根目录下所有url进行 过滤:
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
配置OAuth 流程
- <oauth:authorization-server client-details-service-ref="client-details-service" token-services-ref="tokenServices">
- <oauth:refresh-token/>
- <oauth:password/>
- </oauth:authorization-server>
缺省的token端点是/oauth/token ,只有 password flow 和刷新 token 支持。
保护token端点
使用Spring security 保护token端点:
- <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
- xmlns="http://www.springframework.org/schema/security">
- <anonymous enabled="false"/>
- <http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
- <access-denied-handler ref="oauthAccessDeniedHandler"/>
- </http>
下面配置授权authentication 管理器和客户端服务:
- <bean id="clientCredentialsTokenEndpointFilter"
- class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
- <property name="authenticationManager" ref="clientAuthenticationManager"/>
- </bean>
- <authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
- <authentication-provider user-service-ref="client-details-user-service"/>
- </authentication-manager>
- <bean id="client-details-user-service" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
- <constructor-arg ref="client-details-service" />
- </bean>
配置用户授权服务
Resource Owner Password flow 需要管理用户的授权管理器
- <bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>
- <sec:authentication-manager alias="userAuthenticationManager">
- <sec:authentication-provider user-service-ref="userService">
- <sec:password-encoder ref="passwordEncoder"/>
- </sec:authentication-provider>
- </sec:authentication-manager>
密码 password encoder是用于加密密码。用户服务必须实现一个UserDetailsService ,能根据用户名返回用户。
- @Override
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- notNull(username, "Mandatory argument 'username' missing.");
- User user = userRepository.findByEmailAddress(username.toLowerCase());
- if (user == null) {
- throw new AuthenticationException();
- }
- return user;
- }
配置Token 服务
- <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
- <property name="tokenStore" ref="tokenStore"/>
- <property name="supportRefreshToken" value="true"/>
- <property name="clientDetailsService" ref="client-details-service"/>
- </bean>
保护资源访问
- <oauth:resource-server id="resourceServerFilter" token-services-ref="tokenServices"/>
核心服务
这个服务提供基于访问token获得用户的信息。URL格式:
/v1.0/users/{id}/someresource
- @Path("/v1.0/me")
- @Component
- @Produces({MediaType.APPLICATION_JSON})
- @Consumes({MediaType.APPLICATION_JSON})
- public class MeResource extends BaseResource {
- @RolesAllowed({"ROLE_USER"})
- @GET
- public ApiUser getUser(final @Context SecurityContext securityContext) {
- User requestingUser = loadUserFromSecurityContext(securityContext);
- if(requestingUser == null) {
- throw new UserNotFoundException();
- }
- return new ApiUser(requestingUser);
- }
- protected User loadUserFromSecurityContext(SecurityContext securityContext) {
- OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();
- Object principal = requestingUser.getUserAuthentication().getPrincipal();
- User user = null;
- if(principal instanceof User) {
- user = (User)principal;
- } else {
- user = userRepository.findByEmailAddress((String)principal);
- }
- return user;
- }
- }
测试这个应用,启动:
> ./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服务安全认证的更多相关文章
- Spring Security实现OAuth2.0授权服务 - 进阶版
<Spring Security实现OAuth2.0授权服务 - 基础版>介绍了如何使用Spring Security实现OAuth2.0授权和资源保护,但是使用的都是Spring Sec ...
- 使用Spring Security Oauth2完成RESTful服务password认证的过程
摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...
- Spring Security实现OAuth2.0授权服务 - 基础版
一.OAuth2.0协议 1.OAuth2.0概述 OAuth2.0是一个关于授权的开放网络协议. 该协议在第三方应用与服务提供平台之间设置了一个授权层.第三方应用需要服务资源时,并不是直接使用用户帐 ...
- Spring Security 与 OAuth2 介绍
个人 OAuth2 全部文章 Spring Security 与 OAuth2(介绍):https://www.jianshu.com/p/68f22f9a00ee Spring Security 与 ...
- Spring Security 与 OAuth2(介绍)
https://www.jianshu.com/p/68f22f9a00ee Spring Security 与 OAuth2(介绍) 林塬 2018.01.23 11:14* 字数 3097 阅读 ...
- Spring Security基于Oauth2的SSO单点登录怎样做?一个注解搞定
一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼.本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring ...
- 实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务
本文为实战SpringCloud响应式微服务系列教程第九章,讲解使用Spring WebFlux构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 从本节开始我们 ...
- Spring Security开发安全的REST服务
第1章 课程导学 项目介绍 Java实战:Spring Security开发安全的REST服务,来自慕客网的视频 ,主要讲认证和授权. 企业级的认证和授权 从0开始实现一个可重用的,企业级的,认证和授 ...
- 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 ...
随机推荐
- Revit二次开发示例:AutoUpdate
在Revit打开文件时,修改文件信息.并记录状态,存到log文件中. #region Namespaces using System; using System.Collections.Generic ...
- Ubuntu下修改为永久DNS的方法
安装好Ubuntu之后设置了静态IP地址,再重启后就无法解析域名.想重新设置一下DNS,打开/etc/resolv.conf cat /etc/resolv.conf # Dynamic resolv ...
- Cpp下匿名对象探究
先来看看一段代码: #include "iostream" using namespace std; class ABCD { public: ABCD(int a, int b, ...
- codevs 1005 生日礼物
1005 生日礼物 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 9月12日是小松的朋友小寒的生日.小松知 ...
- oracle复杂查询是sql
一.over()分析函数 分组查前几条:select * from test t where (select count(*) from test a where t.type=a.type and ...
- 在IDEA(phpStorm)中使用Babel编译ES6
安装Babel 官方文档建议我们根据单个项目进行本地安装,原因是不同的项目可以依赖不同版本的 Babel,使你的项目更方便移植.更易于安装. 在项目的根目录下使用命令行工具(CMD等)执行下面代码 n ...
- Go语言Web框架gwk介绍 (一)
今天看到Golang排名到前30名了,看来关注的人越来越多了,接下来几天详细介绍Golang一个web开发框架GWK. 现在博客园支持markdown格式发布文章么?后台的编辑器不太好用嘛. GWK ...
- 小程序获取openid unionid session_key
.wxml <button bindtap="paytap">授权</button> .js Page({ paytap: function () { va ...
- oracle存储过程获取异常信息码和异常信息
oracle存储过程,可以通过sqlcode 获取异常编码.通过sqlerrm获取异常信息. 例子: create or replace procedure write2blob(p_id in nu ...
- spring mvc接收数组
(一)前言 对于springmvc接收数组的问题啊,我试验过几次,但是了有时候成功了,有时候失败了,也不知道为啥的,然后现在又要用到了,所以打算具体看看到底怎么回事,但是了我实验成功了顺便找了好多资料 ...