Spring Security Oauth2 的配置
使用oauth2保护你的应用,可以分为简易的分为三个步骤
- 配置资源服务器
- 配置认证服务器
- 配置spring security
前两点是oauth2的主体内容,但前面我已经描述过了,spring security oauth2是建立在spring security基础之上的,所以有一些体系是公用的。
oauth2根据使用场景不同,分成了4种模式
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
本文重点讲解接口对接中常使用的密码模式(以下简称password模式)和客户端模式(以下简称client模式)。授权码模式使用到了回调地址,是最为复杂的方式,通常网站中经常出现的微博,qq第三方登录,都会采用这个形式。简化模式不常用。
配置资源服务器和授权服务器
由于是两个oauth2的核心配置,我们放到一个配置类中。
为了方便下载代码直接运行,我这里将客户端信息放到了内存中,生产中可以配置到数据库中。token的存储一般选择使用redis,一是性能比较好,二是自动过期的机制,符合token的特性。
@Configuration
public class OAuth2ServerConfig { private static final String RESOURCE_ID = "wymlib";
//资源配置服务器
@Configuration
@EnableResourceServer
@Order(110)
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(true);
} @Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers().anyRequest()
.and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/api/v1/**").authenticated();//配置访问控制,必须认证过后才可以访问
}
} //认证服务器
@Configuration
@EnableAuthorizationServer
@Order(99)
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired
AuthenticationManager authenticationManager;
// @Autowired
// RedisConnectionFactory redisConnectionFactory; @Value("${config.oauth2.clientID}")
String clientID; @Value("${config.oauth2.clientSecret}")
String clientSecret; @Value("${config.oauth2.accessTokenValiditySeconds}")
int accessTokenValiditySeconds; @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(clientID)
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("client")
.secret(clientSecret)
.accessTokenValiditySeconds(accessTokenValiditySeconds);
} /*@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager);
}*/ @Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
} } }
简单说下spring security oauth2的认证思路。
client模式,没有用户的概念,直接与认证服务器交互,用配置中的客户端信息去申请accessToken,客户端有自己的client_id,client_secret对应于用户的username,password,而客户端也拥有自己的authorities,当采取client模式认证时,对应的权限也就是客户端自己的authorities。
password模式,自己本身有一套用户体系,在认证时需要带上自己的用户名和密码,以及客户端的client_id,client_secret。此时,accessToken所包含的权限是用户本身的权限,而不是客户端的权限。
我对于两种模式的理解便是,如果你的系统已经有了一套用户体系,每个用户也有了一定的权限,可以采用password模式;如果仅仅是接口的对接,不考虑用户,则可以使用client模式。
配置spring security
在spring security的版本迭代中,产生了多种配置方式,建造者模式,适配器模式等等设计模式的使用,spring security内部的认证flow也是错综复杂,在我一开始学习ss也产生了不少困惑,总结了一下配置经验:使用了springboot之后,spring security其实是有不少自动配置的,我们可以仅仅修改自己需要的那一部分,并且遵循一个原则,直接覆盖最需要的那一部分。这一说法比较抽象,举个例子。比如配置内存中的用户认证器。有两种配置方式
planA: @Bean
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
return manager;
}
planB: @Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user_1").password("123456").authorities("USER")
.and()
.withUser("user_2").password("123456").authorities("USER");
} @Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
}
你最终都能得到配置在内存中的两个用户,前者是直接替换掉了容器中的UserDetailsService,这么做比较直观;后者是替换了AuthenticationManager,当然你还会在SecurityConfiguration 复写其他配置,这么配置最终会由一个委托者去认证。如果你熟悉spring security,会知道AuthenticationManager和AuthenticationProvider以及UserDetailsService的关系,他们都是顶级的接口,实现类之间错综复杂的聚合关系…配置方式千差万别,但理解清楚认证流程,知道各个实现类对应的职责才是掌握spring security的关键。
下面给出我最终的配置:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
return manager;
} @Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/*").permitAll();
// @formatter:on
}
}
重点就是配置了一个UserDetailsService,和ClientDetailsService一样,为了方便运行,使用内存中的用户,实际项目中,一般使用的是数据库保存用户,具体的实现类可以使用JdbcDaoImpl或者JdbcUserDetailsManager。
xml配置:
<!-- OAuth2 URL: /oauth/token 的处理与配置 一般使用时这里不需要修改, 直接使用即可 -->
<sec:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="oauth2AuthenticationManager"
entry-point-ref="oauth2AuthenticationEntryPoint" use-expressions="false">
<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="oauth2AuthenticationEntryPoint" />
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauth2AccessDeniedHandler" />
<!-- <csrf disabled="true"/> -->
</sec:http> <sec:authentication-manager id="oauth2AuthenticationManager">
<sec:authentication-provider
user-service-ref="oauth2ClientDetailsUserService" />
</sec:authentication-manager> <beans:bean id="oauth2ClientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<beans:constructor-arg ref="clientDetailsService" />
</beans:bean> <!-- 管理 ClientDetails -->
<beans:bean id="clientDetailsService"
class="org.springframework.security.oauth2.provider.client.JdbcClientDetailsService">
<beans:constructor-arg index="0" ref="dataSource" />
</beans:bean> <beans:bean id="oauth2AuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint" /> <!-- 处理grant_type=client_credentials 的逻辑 只从请求中获取client_id与client_secret -->
<beans:bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<beans:property name="authenticationManager" ref="oauth2AuthenticationManager" />
</beans:bean> 拦截api/v1: <sec:http pattern="/api/v1/**" create-session="never"
entry-point-ref="oauth2AuthenticationEntryPoint"
access-decision-manager-ref="oauth2AccessDecisionManager"
use-expressions="false">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/api/v1/**"
access="IS_AUTHENTICATED_ANONYMOUSLY" />
<sec:custom-filter ref="unityResourceServer" before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauth2AccessDeniedHandler" />
</sec:http> <!-- 扩展Spring Security 默认的 AccessDecisionManager 添加对OAuth中 scope 的检查与校验 -->
<beans:bean id="oauth2AccessDecisionManager"
class="org.springframework.security.access.vote.UnanimousBased">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<beans:bean class="org.springframework.security.access.vote.RoleVoter" />
<beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</beans:list>
</beans:constructor-arg>
</beans:bean> <!-- 每一个资源(resource)的定义, resource-id必须唯一, OauthClientDetails中的resourceIds属性的值由此来的,
允许一个Client有多个resource-id, 由逗号(,)分隔 每一个定义会在Security Flow中添加一个位于 PRE_AUTH_FILTER
之前的Filter -->
<!--unity resource server filter -->
<oauth2:resource-server id="unityResourceServer"
resource-id="unity-resource" token-services-ref="tokenServices" /> <beans:bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<beans:property name="tokenStore" ref="tokenStore" />
<beans:property name="clientDetailsService" ref="clientDetailsService" />
<beans:property name="supportRefreshToken" value="true" />
<beans:property name="accessTokenValiditySeconds" value="30" />
</beans:bean> <!--Config token services -->
<!--<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/> -->
<beans:bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<beans:constructor-arg index="0" ref="dataSource" />
</beans:bean> <!-- Security OAuth Flow的核心配置 每一个配置对应一类具体的grant_type 可根据需求删除或禁用, 如: <oauth2:implicit
disabled="true"/> 默认支持OAuth2提供的5类grant_type, 若不需要任何一类, 将其配置注释掉(或删掉)即可. 若需要自定义
authorization url, 在 <oauth2:authorization-server > 配置中添加authorization-endpoint-url,如:
authorization-endpoint-url="/oauth2/authorization" 若需要自定义 token url, 在 <oauth2:authorization-server
> 配置中添加token-endpoint-url配置, 如:token-endpoint-url="/oauth2/my_token" -->
<oauth2:authorization-server
client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
user-approval-handler-ref="oauthUserApprovalHandler"
user-approval-page="oauth_approval" error-page="oauth_error">
<oauth2:authorization-code authorization-code-services-ref="jdbcAuthorizationCodeServices" />
<oauth2:implicit />
<oauth2:refresh-token />
<oauth2:client-credentials />
<oauth2:password />
</oauth2:authorization-server> <beans:bean id="oauthUserApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
<beans:property name="tokenStore" ref="tokenStore" />
<beans:property name="clientDetailsService" ref="clientDetailsService" />
<beans:property name="requestFactory" ref="oAuth2RequestFactory" />
<!-- <beans:property name="oauthService" ref="oauthService"/> -->
</beans:bean> <!-- 管理 Authorization code -->
<beans:bean id="jdbcAuthorizationCodeServices"
class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
<beans:constructor-arg index="0" ref="dataSource" />
</beans:bean> 请求token的url:
http://192.9.8.144/ymlib/oauth/token?client_id=aa086e67c36342ed9ab6519247f5b68b&client_secret=mQDUMa03Rdy5vcMWjYHJmitkWJi3Rosr&grant_type=client_credentials
参考资料:
从零开始的Spring Security Oauth2(一)
从零开始的Spring Security Oauth2(二)
从零开始的Spring Security Oauth2(三)
Spring Security Oauth2 的配置的更多相关文章
- Spring Security OAuth2 Demo -- good
1. 添加依赖授权服务是基于Spring Security的,因此需要在项目中引入两个依赖: <dependency> <groupId>org.springframework ...
- Spring Security OAuth2 Demo
Spring Security OAuth2 Demo 项目使用的是MySql存储, 需要先创建以下表结构: CREATE SCHEMA IF NOT EXISTS `alan-oauth` DEFA ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- Spring Security OAuth2之resource_id配置与验证
一.resource_id的作用 Spring Security OAuth2 架构上分为Authorization Server认证服务器和Resource Server资源服务器.我们可以为每一个 ...
- 【OAuth2.0】Spring Security OAuth2.0篇之初识
不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...
- 使用Spring Security Oauth2完成RESTful服务password认证的过程
摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息.ClientDetails.token存入数据库而非内存.配置 ...
- Spring security oauth2最简单入门环境搭建
关于OAuth2的一些简介,见我的上篇blog:http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉.. 友情提示 学习曲线:spring+s ...
- Spring Security Oauth2系列(一)
前言: 关于oauth2,其实是一个规范,本文重点讲解spring对他进行的实现,如果你还不清楚授权服务器,资源服务器,认证授权等基础概念,可以移步理解OAuth 2.0 - 阮一峰,这是一篇对于oa ...
- Spring Security Oauth2 permitAll()方法小记
黄鼠狼在养鸡场山崖边立了块碑,写道:"不勇敢地飞下去,你怎么知道自己原来是一只搏击长空的鹰?!" 从此以后 黄鼠狼每天都能在崖底吃到那些摔死的鸡! 前言 上周五有网友问道,在使用s ...
随机推荐
- Linux之部署前后端分离项目
首先得看我前两个博客,把python3,虚拟环境,mariadb数据库,redis数据库,nginx安装好. 一.创建一个虚拟环境 1,创建虚拟环境 mkvirtualenv zijin #创建了一个 ...
- [转]【翻译】在Visual Studio中使用Asp.Net Core MVC创建你的第一个Web API应用(一)
本文转自:https://www.cnblogs.com/inday/p/6288707.html HTTP is not just for serving up web pages. It’s al ...
- 一篇文章搞定SpringMVC参数绑定
SpringMVC参数绑定,简单来说就是将客户端请求的key/value数据绑定到controller方法的形参上,然后就可以在controller中使用该参数了 下面通过5个常用的注解演示下如何进行 ...
- Java开发笔记(七十八)面向对象的后门——反射
作为一门面向对象的编程语言,Java认为一切皆是对象,每个对象都能归属于某个类,甚至每个类均可提取出一种特殊的类型,即Class类型.早在前面介绍多态的时候,就提到每个类都存在独一无二的基因,通过比较 ...
- testlib.h从入门到入坟
学了这么久OI连个spj都不会写真是惭愧啊... 趁着没退役赶紧学一波吧 配置 github下载地址 我是直接暴力复制粘贴的.. 然后扔到MingW的目录里 直接引用就好啦 基本语法 引用testli ...
- 36.Odoo产品分析 (四) – 工具板块(6) – 午餐管理(2)
查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (四) – 工具板块(6) – 午餐管理(1) 4 查看订单 点击"之前的订单",可以看到刚才的订单信息: 点击右边的 ...
- SpringBoot 配置 跨域支持
跨域资源共享(CORS,请求协议,请求地址,请求端口三者必须相同才是同一服务器,否则都要进行跨域操作)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源.另外,规范要求,对那 ...
- java之网络爬虫介绍
文章大纲 一.网络爬虫基本介绍二.java常见爬虫框架介绍三.WebCollector实战四.项目源码下载五.参考文章 一.网络爬虫基本介绍 1. 什么是网络爬虫 网络爬虫(又被称为网页蜘蛛, ...
- spring学习总结——高级装配学习二(处理自动装配的歧义性)
我们已经看到如何使用自动装配让Spring完全负责将bean引用注入到构造参数和属性中.自动装配能够提供很大的帮助.不过,spring容器中仅有一个bean匹配所需的结果时,自动装配才是有效的.如果不 ...
- MySQL:1366 - Incorrect string value错误解决办法
今天使用navicat向MySQL中插入中文时,报错: - Incorrect string value:... 在我自己数据库设计之初,没有设计好字符编码格式的问题. 使用如下语句解决: alter ...