Spring Security OAuth2学习
什么是 oAuth
oAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。
什么是 Spring Security
Spring Security 是一个安全框架,前身是 Acegi Security,能够为 Spring 企业应用系统提供声明式的安全访问控制。Spring Security 基于 Servlet 过滤器、IOC 和 AOP,为 Web 请求和方法调用提供身份确认和授权处理,避免了代码耦合,减少了大量重复代码工作。
客户端授权模式
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。oAuth 2.0 定义了四种授权方式。
- implicit:简化模式,不推荐使用
- authorization code:授权码模式(本文章采用这种模式)
- resource owner password credentials:密码模式
- client credentials:客户端模式
个人对oAuth2认证过程的理解
编码部分
- 创建项目工程,本文章采用多module方式
主要是配置一个pom,文章结束后代码会发到github,这里不复制了 - 创建统一依赖
统一依赖中配置org.springframework.cloud
springboot2.1.x需要使用Greenwich版本 - 创建认证服务器模块(内存模拟版本)
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
// 注入 WebSecurityConfiguration 中配置的 BCryptPasswordEncoder
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder.encode("secret"))
// 授权类型
.authorizedGrantTypes("authorization_code")
// 授权范围
.scopes("app")
// 注册回调地址
.redirectUris("http://blog.yhhu.xyz");
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 设置默认的加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
// 在内存中创建用户并为密码加密
.withUser("user").password(passwordEncoder().encode("123456")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN");
}
}
SpringSecurity提供默认的登录界面,并且提供RestfulAPI
spring:
application:
name: oauth2-server
server:
port: 8080
==必须遵循这样的请求url==
获取code:在浏览器直接访问http://localhost:8080/oauth/authorize?client_id=client&response_type=code
通过code获取access_token:http://client:secret@localhost:8080/oauth/token
这里需要带两个参数,采用x-www-form-urlencoded方式,grant_type为authorization_code,code为上一步获取到的内容
url说明:client:secret不是固定的,这取决于你在AuthorizationServerConfiguration中withClient和secret的设置
- 创建认证服务器模块(JDBC版本)
- 创建一个oauth数据库,官方给的表示H2的,mysql有点改动,百度找了一个
CREATE TABLE `clientdetails` ( `appId` varchar(128) NOT NULL, `resourceIds` varchar(256) DEFAULT NULL, `appSecret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `grantTypes` varchar(256) DEFAULT NULL, `redirectUrl` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additionalInformation` varchar(4096) DEFAULT NULL, `autoApproveScopes` varchar(256) DEFAULT NULL, PRIMARY KEY (`appId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_access_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, `authentication` blob, `refresh_token` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_approvals` ( `userId` varchar(256) DEFAULT NULL, `clientId` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `status` varchar(10) DEFAULT NULL, `expiresAt` timestamp NULL DEFAULT NULL, `lastModifiedAt` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_client_details` ( `client_id` varchar(128) NOT NULL, `resource_ids` varchar(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_client_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar(128) NOT NULL, `user_name` varchar(256) DEFAULT NULL, `client_id` varchar(256) DEFAULT NULL, PRIMARY KEY (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_code` ( `code` varchar(256) DEFAULT NULL, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_refresh_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication` blob ) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 操作一下oauth_client_details表
需要设置的内容为client_id,client_secret,scope,authorized_grant_types,web_server_redirect_uri,注意client_secret是经过加密的
至此完成验证功能,其他操作和内存版都是一样的,完成后可以在数据库中看到看到token相关信息
RBAC(Role-Based Access Control,基于角色的访问控制)
增加了几个表,模型如下
可以做的简单一点,可是这样做功能更加完备,单单在user表中插入role列的话不便于管理
下面开始具体实现
- 在认证服务器端排除token检查的权限判断
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/check_token");
}
创建一个service,查询用户使用(代码略)
将授权模式由内存模式改为userDetailService方式
@Autowired
private DataSource dataSource;
@Bean
public TokenStore tokenStore() {
// 基于 JDBC 实现,令牌保存到数据
return new JdbcTokenStore(dataSource);
}
@Bean
public ClientDetailsService jdbcClientDetails() {
// 基于 JDBC 实现,需要事先在数据库配置客户端信息
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
{
// 设置令牌
endpoints.tokenStore(tokenStore());
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
- 认证中心UserDetail权限判断逻辑
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private TbUserService tbUserService;
@Autowired
private TbPermissionService tbPermissionService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
TbUser tbUser = tbUserService.getByUsername(s);
List<GrantedAuthority> grantedAuthorities= Lists.newArrayList();
if (tbUser != null) {
List<TbPermission> tbPermissions = tbPermissionService.selectByUserId(tbUser.getId());
tbPermissions.forEach(item -> {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(item.getEnname());
grantedAuthorities.add(grantedAuthority);
});
return new User(tbUser.getUsername(),tbUser.getPassword(),grantedAuthorities);
}
return null;
}
}
- 如何自定义一个权限控制方法?(通过permission中url对应的方式来判断权限)
定义权限判断类
@Component("permission")
public class PermissionCheck {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
boolean hasPermission = false;
//取出当前token对应用户所拥有的权限
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (antPathMatcher.match(authority.getAuthority(), request.getRequestURI())) {
hasPermission = true;
break;
}
}
return hasPermission;
}
}
在资源服务器中配置access("")方法来调用hasPermission方法
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.and()
//管理session的创建策略永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//ALWAYS总是创建HttpSession
//IF_REQUIREDSpring Security只会在需要时创建一个HttpSession
//NEVERSpring Security不会创建HttpSession,但如果它已经存在,将可以使用HttpSession
.and()
.authorizeRequests()
.anyRequest().access("@permission.hasPermission(request,authentication)");
// 以下为配置所需保护的资源路径及权限,需要与认证服务器配置的授权部分对应
// .antMatchers("/**").hasAuthority("SystemContent");
}
}
补充一个错误:Spring security oauth2 throwing "no bean resolver registered" for custom bean
在ResourceServerConfiguration类下补充以下配置
/**
* 以下为采坑地方,如果不使用自己的动态权限控制,下面无需配置也能运行
* 原因暂时不明,解决方案为参考以下issues地址
* https://github.com/spring-projects/spring-security-oauth/issues/730#issuecomment-219480394
*/
@Autowired
private OAuth2WebSecurityExpressionHandler expressionHandler;
@Bean
public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
expressionHandler.setApplicationContext(applicationContext);
return expressionHandler;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.expressionHandler(expressionHandler);
}
完整代码地址
github:oauth2
Spring Security OAuth2学习的更多相关文章
- Spring Cloud 学习 (十) Spring Security, OAuth2, JWT
通过 Spring Security + OAuth2 认证和鉴权,每次请求都需要经过 OAuth Server 验证当前 token 的合法性,并且需要查询该 token 对应的用户权限,在高并发场 ...
- 【OAuth2.0】Spring Security OAuth2.0篇之初识
不吐不快 因为项目需求开始接触OAuth2.0授权协议.断断续续接触了有两周左右的时间.不得不吐槽的,依然是自己的学习习惯问题,总是着急想了解一切,习惯性地钻牛角尖去理解小的细节,而不是从宏观上去掌握 ...
- Spring Security Oauth2系列(一)
前言: 关于oauth2,其实是一个规范,本文重点讲解spring对他进行的实现,如果你还不清楚授权服务器,资源服务器,认证授权等基础概念,可以移步理解OAuth 2.0 - 阮一峰,这是一篇对于oa ...
- spring security oauth2 jwt 认证和资源分离的配置文件(java类配置版)
最近再学习spring security oauth2.下载了官方的例子sparklr2和tonr2进行学习.但是例子里包含的东西太多,不知道最简单最主要的配置有哪些.所以决定自己尝试搭建简单版本的例 ...
- Spring Security OAuth2 SSO
通常公司肯定不止一个系统,每个系统都需要进行认证和权限控制,不可能每个每个系统都自己去写,这个时候需要把登录单独提出来 登录和授权是统一的 业务系统该怎么写还怎么写 最近学习了一下Spring Sec ...
- Spring Security Oauth2 的配置
使用oauth2保护你的应用,可以分为简易的分为三个步骤 配置资源服务器 配置认证服务器 配置spring security 前两点是oauth2的主体内容,但前面我已经描述过了,spring sec ...
- Re:从零开始的Spring Security Oauth2(一)
前言 今天来聊聊一个接口对接的场景,A厂家有一套HTTP接口需要提供给B厂家使用,由于是外网环境,所以需要有一套安全机制保障,这个时候oauth2就可以作为一个方案. 关于oauth2,其实是一个规范 ...
- Spring Security OAuth2 Demo —— 授权码模式
本文可以转载,但请注明出处https://www.cnblogs.com/hellxz/p/oauth2_oauthcode_pattern.html 写在前边 在文章OAuth 2.0 概念及授权流 ...
- Spring Security 解析(七) —— Spring Security Oauth2 源码解析
Spring Security 解析(七) -- Spring Security Oauth2 源码解析 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因 ...
随机推荐
- A1100 Mars Numbers (20 分)
一.技术总结 这一题可以使用map进行想打印存储,因为数据量不是很大,最后直接输出.但是还是觉得没有必要. 主要考虑两个问题,首先是数字转化为字符串,实质就是进制转化,但是有点不同,如果十位有数字,个 ...
- 基于Django的Rest Framework框架的响应器
本文目录 一 作用 二 内置渲染器 三 局部使用 四 全局使用 五 自定义显示模版 回到目录 一 作用 根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件.用户请求URL: ht ...
- 【shell脚本】对100以内的所有正整数相加求和(1+2+3+4...+100)===sum.sh
对100以内的所有正整数相加求和(1+2+3+4...+100) [root@VM_0_10_centos shellScript]# cat sum.sh #!/bin/bash # 对100以内的 ...
- 15-scrapy-redis两种形式分布式爬虫
什么叫做分布式爬虫? 分布式爬虫,就是多台机器共用一个scrapy—redis程序高效爬取数据, 为啥要用分布式爬虫? 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器 ...
- 微信小程序 没有找到可以构建的npm包
问题如图: 1.进入小程序根目录,打开cmd,输入:npm init:然后,输入命令后一直点回车 2.输入命令:npm i vant-weapp -S --production 执行命令完之后,然后再 ...
- vue 仿微信朋友圈9张图上传功能
项目需求要求用户上传商品的时候可以一次性上传9张图,多余9张提示‘只能上传9张图’,并且每张图右上角有个删除按钮,图片也可以点击放大. 出来的效果图如下: 话不多说,上代码: <el-form- ...
- 使用DataV制作的一个数据报表
之前接到一个做数据报表的需求,当时准备使用echarts自己画.后来考虑时间来不及,着急要,再加上一直在使用阿里云的产品,就在阿里云上个找了找数据大屏的服务.于是很快做出了一款. 然后看到 https ...
- centos7开启,关闭防火墙
1.查看防火墙状态 firewall-cmd --state 2.停止防火墙 systemctl stop firewalld.service 3.禁止防火墙开机启动 systemctl disabl ...
- Python中最常用的字符串方法!
字符串是字符序列.Python中内置的string类代表基于Unicode国际字符集的字符串.除了Python中常见的操作外,字符串还有一些专属于它们的附加方法.下图显示了所有这些可用的方法: Pyt ...
- Python 内置函数补充匿名函数
Python3 匿名函数 定义一个函数与变量的定义非常相似,对于有名函数,必须通过变量名访问 def func(x,y,z=1): return x+y+z print(func(1,2,3)) 匿名 ...