上一篇文章:Spring Security + OAuth2.0项目搭建:https://blog.csdn.net/qq_42402854/article/details/123057625

接着认识 Oauth2的四种授权模式。

一、Oauth2认证服务

在项目中,我们采用将用户,客户端,token等数据保存在数据库中。

1、认证服务具体配置

1.1 YML配置文件

server:port: 18091# jsp配置
spring:application:# 服务实例名,每个服务名必须唯一name: OAUTH-SERVERdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/security_authority?useUnicode=true;characterEncoding=utf8;useSSL=true;serverTimezone=GMTusername: rootpassword: 123456main:allow-bean-definition-overriding: true #允许我们自己覆盖spring放入到IOC容器的对象# mybatis配置
mybatis:configuration:map-underscore-to-camel-case: truemapper-locations: classpath:mybatis/mapper/*.xmllogging:level:com.charge.learn.springsecurity.oauth2.parent.oauth.dao: debugeureka:instance:# 服务主机名称hostname: localhostclient:service-url:defaultZone: http://admin:1qaz2wsx@${eureka.instance.hostname}:18090/eureka/

1.2 SpringSecurity配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true) // 开启SpringSecurity权限注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate SysUserService userService;// 密码解析器注入IOC容器@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}// 指定认证对象的来源@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userService).passwordEncoder(passwordEncoder());}// SpringSecurity配置信息@Overridepublic void configure(HttpSecurity http) throws Exception {// 这里简单点,使用默认的认证页面http.authorizeRequests().anyRequest().authenticated() //所有资源必须授权后访问.and().formLogin().loginProcessingUrl("/login").permitAll() //指定认证页面可以匿名访问.and().csrf()//关闭跨站请求防护.disable();}/*** AuthenticationManager对象在OAuth2认证服务中要使用,提前注入IOC容器中,即就变成了 Oauth2授权认证。只给授权码模式使用,其他模式可以不写* @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

1.3 OAuth2授权配置类

@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {//数据库连接池对象@Autowiredprivate DataSource dataSource;//认证业务对象@Autowiredprivate SysUserService userService;//授权模式专用对象@Autowiredprivate AuthenticationManager authenticationManager;//客户端信息来源@Beanpublic JdbcClientDetailsService jdbcClientDetailsService(){return new JdbcClientDetailsService(dataSource);}//token保存策略@Beanpublic TokenStore tokenStore(){return new JdbcTokenStore(dataSource);}//授权信息保存策略@Beanpublic ApprovalStore approvalStore(){return new JdbcApprovalStore(dataSource);}//授权码模式数据来源@Beanpublic AuthorizationCodeServices authorizationCodeServices(){return new JdbcAuthorizationCodeServices(dataSource);}//指定客户端信息的数据库来源@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(jdbcClientDetailsService());}//检查token的策略@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.allowFormAuthenticationForClients(); // 可接受form表单提交security.checkTokenAccess("permitAll()"); // 允许校验 token,匿名访问}/*** OAuth2的主配置信息,将上面所有配置都整合注册进来* @param endpoints* @throws Exception*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.userDetailsService(userService) // 刷新 token, 不配置的话,刷新token是使用不了的.approvalStore(approvalStore()).authenticationManager(authenticationManager) //认证管理器.authorizationCodeServices(authorizationCodeServices()) //授权码服务.tokenStore(tokenStore()); //令牌管理服务}}

1.3.1 默认端点 URL
框架默认的URL链接有如下几个:

  • /oauth/authorize : 授权端点
  • /auth/token : 令牌端点
  • /oauth/confirm_access :用户确认授权提交的端点
  • /oauth/error : 授权服务错误信息端点。
  • /oauth/check_token :用于资源服务访问的令牌进行解析的端点。
  • /oauth/token_key : 使用Jwt令牌需要用到的提供公有密钥的端点。

需要注意的是,这几个授权端点应该被Spring Security保护起来只供授权用户访问。

也可以通过 pathMapping()方法来自定义端点URL地址来替代默认端点 URL。

   @Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints//.pathMapping("/oauth/confirm_access","/customer/confirm_access")//定制授权同意页面.authenticationManager(authenticationManager)//认证管理器
。。。}

1.4 用户和角色

用户和角色/权限的类和使用数据库认证授权一样。这里简单截几个图。

注意:这里必须将用户和角色类放到公共模板中,认证服务和资源服务使用的是同一个用户和角色对象。否则会报 token反序列化问题。

1.5 启动类

@SpringBootApplication
@MapperScan("com.charge.learn.springsecurity.oauth2.parent.oauth.dao")
@EnableEurekaClient
public class OAuthServerApplication {public static void main(String[] args) {SpringApplication.run(OAuthServerApplication.class, args);}
}

启动项目,ok。

在 oauth_client_details表中插入一条数据:

INSERT INTO `oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`)
VALUES ('charge8_all', 'order_source_api', '$2a$10$CYX9OMv0yO8wR8rE19N2fOaXDJondci5uR68k2eQJm50q8ESsDMlC', 'read,write', 'client_credentials,implicit,authorization_code,refresh_token,password', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false');

2、四种授权模式

OAuth2定义了四种授权方式,其实是代表了OAuth授权第三方的不同互信程度。

四种授权模式(authorization grant):

  • Authorization code Grant:授权码模式,推荐使用
  • Implicit Grant:隐藏/简化模式,不推荐使用
  • Resource Owner Password Credentials Grant:密码模式
  • Client Credentials Grant:客户端模式

客户端(第三方应用)必须得到资源所有者(用户)的同意授权(authorization grant)以后,才能向客户端颁发令牌(access token)。客户端通过令牌,就可以访问资源服务器,请求数据。

注意:不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

图来自参考文章。

2.1 授权码模式

授权码(authorization code)方式:指的是第三方应用先申请一个授权码,然后再用该授权码获取令牌。
授权码只能使用一次。

授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

授权码模式是 OAuth2中安全性最高,应用场景最广泛的模式,它适用于那些有后端的 Web 应用。常见的微信,QQ等第三方登录也可采用这种方式实现。

一般流程:

第一步: A 网站跳转 B 网站

A 网站提供一个链接,用户点击后就会跳转到 B 网站,进行授权用户数据给 A 网站使用。

跳转链接URL如下:

http://localhost:18091/oauth/authorize?response_type=code&client_id=charge8_all

参数说明:

  • response_type参数:表示要求返回授权码(code),
  • client_id参数:客户端id,让 B 网站知道是谁在请求,
  • redirect_uri参数:是 B 网站接受或拒绝请求后的跳转网址,即回调地址,
  • scope参数:表示要求的授权范围(读,写)。

之后会调转到登录接口,输入用户名密码。

第二步:B 网站会要求用户登录,并询问是否同意给予 A 网站授权

用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。

用户表示同意,这时 B 网站就会跳转到回调地址(redirect_uri参数指定的网址)。跳转时会携带一个授权码,传给 A 网站。

回调地址URL:

  • code参数:表示授权码。

注意:这个回调地址应该回调给 A系统, code就是授权码。授权码只能使用一次。
测试时,报错oauth_code表的authentication数据内容超过大小了,这里把它改成 longblob类型即可。

第三步:A 网站通过授权码向 B 网站请求令牌

A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌(access token)。

后端申请令牌URL如下:

参数说明:

  • client_id参数:客户端id
  • client_secret参数:客户端秘钥,用来让 B 网站确认 A 的身份(client_secret参数是保密的,因此只能在后端发请求),
  • grant_type参数:值是AUTHORIZATION_CODE,表示采用的授权方式是授权码,
  • code参数:是上一步拿到的授权码,
  • redirect_uri参数:是令牌颁发后的回调网址。一定和申请授权码时用的redirect_uri一致。数据在数据库,可以不填。

第四步:B 网站颁发令牌
B 网站收到请求令牌以后,就会颁发令牌。具体做法是向 redirect_uri指定的网址,发送一段 JSON 数据。如上图。access_token字段就是令牌,A 网站在后端拿到了令牌,就可以访问资源服务了。

当再次点击时,会报错,说明code只能使用一次。

{"error": "invalid_grant","error_description": "Invalid authorization code: LibMDT"
}

2.2 简化模式

简化模式整个过程没有授权码这个中间步骤,允许直接向前端颁发令牌,即token直接暴露在浏览器。所以又称为(授权码)“隐藏式”(implicit)。

简化模式适用于纯前端页面应用,没有后端。
所谓纯静态页面应用,也就是应用没有在服务器上执行代码的权限(通常是把代码托管在别人的服务器上),只有前端 JS 代码的控制权。

这种方式把令牌直接传给前端,是很不安全的。
因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间(session)有效,浏览器关掉,令牌就失效了。

一般流程:

第一步:A 网站跳转 B 网站
A 网站提供一个链接,要求用户跳转到 B 网站,进行授权用户数据给 A 网站使用。

跳转链接URL如下:

http://localhost:18091/oauth/authorize?response_type=token&client_id=charge8_all

参数说明:

  • response_type参数:为token,表示要求直接返回令牌。

之后会调转到登录接口,输入用户名密码。

第二步:B 网站会要求用户登录,并询问是否同意给予 A 网站授权
用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。
用户表示同意,这时 B 网站就会跳转到回调地址(redirect_uri参数指定的网址)。跳转时,并且把令牌作为 URL 参数,传给 A 网站。

由于上面用户已经登录过了,所以这里直接返回了token。如果是新窗口,还是需要登录(同上)之后才会返回了token。

回调地址URL:

https://www.baidu.com/#access_token=9b0e9571-bb81-4226-a873-d898b266586a&token_type=bearer&expires_in=42784&scope=read%20write
  • token参数:就是令牌,直接向前端(A 网站)颁发令牌。

注意:令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。

2.3 密码模式

密码模式,用户把自己用户名和密码直接告诉客户端(客户端不得储存密码),客户端使用这些信息申请令牌。

这种方式需要用户给出自己的用户名/密码,显然风险很大,因此只适用于其他授权方式都无法采用的情况,而且必须是用户高度信任的应用。

一般流程:

第一步:A 应用向 B 应用发出请求令牌。
A 应用要求用户提供 B 应用的用户名和密码。拿到信息以后,A 应用就直接向 B 应用请求令牌。

参数说明:

  • grant_type参数:是授权方式,这里的password表示"密码式",
  • username参数:是 B 的用户名,
  • password参数:是 B 的用户密码。
  • client_id参数:客户端id
  • client_secret参数:客户端秘钥

第二步:B 网站验证用户身份,并直接颁发令牌

B 网站验证用户身份通过后,直接颁发令牌。
注意:这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应, A 因此拿到令牌。

{"access_token": "9b0e9571-bb81-4226-a873-d898b266586a","token_type": "bearer","refresh_token": "9e48fec7-b6df-46db-84d2-7e8e79019652","expires_in": 42294,"scope": "read write"
}

2.4 客户端模式

客户端模式就是直接对客户端进行身份验证,验证通过后,颁发token。

这种模式其实有点不太属于OAuth2的范畴了。客户端(A 网站)完全脱离用户,以自己的身份去向 B 网站申请 token。
换言之,用户无需具备B服务的使用权也可以。完全是A服务与B服务内部的交互,与用户无关了。
适用于没有前端,没有用户界面的后端模块。

一般流程:

第一步:A 应用向 B 应用发出请求令牌。
A 应用向 B 应用发出请求令牌。

参数说明:

  • grant_type参数:值为client_credentials表示采用凭证式,
  • client_id参数:客户端id,
  • client_secret参数:客户端秘钥,用来让 B 确认 A 的身份。

第二步:B 应用验证通过以后,直接返回令牌。
B 应用验证通过以后,直接返回令牌。

{"access_token": "d68df8cc-8643-44e2-ae17-1e85933100d6","token_type": "bearer","expires_in": 43199,"scope": "read write"
}

这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

3、令牌的使用

3.1 更新令牌

令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许用户自动更新令牌。

注意:除了客户端模式,其他几个模式都可以刷新 token。

首先,需要开启刷新token,OAuth2授权配置类中加上它:

  • .userDetailsService(userService) // 刷新 token, 不配置的话,刷新token是使用不了的

重启认证服务。

令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

这里使用密码模式令牌来测试,刷新 token。

参数说明:

  • grant_type参数:值为refresh_token表示要求更新令牌,
  • client_id参数:客户端id
  • client_secret参数:客户端秘钥,用来让 B 确认 A 的身份,
  • refresh_token参数:就是用于更新令牌的令牌。

B 网站验证通过以后,就会颁发新的令牌。就可以通过新的令牌来访问资源服务。

3.2 检查token

/oauth/check_token端点,此接口没有允许,默认是不允许访问。
报错如下:

{"timestamp": "2022-02-23T15:37:14.715+0000","status": 401,"error": "Unauthorized","message": "Unauthorized","path": "/oauth/check_token"
}

所以我们需要设置该接口允许访问。
方式1:

// AuthorizationServerConfigurerAdapter@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {//允许表单认证oauthServer.allowFormAuthenticationForClients();oauthServer.checkTokenAccess("permitAll()");}

方法2:
security.oauth2.authorization.check-token-access=permitAll()

3.3 通过令牌请求资源服务

A 应用拿到令牌(access token)以后,就可以向 B 引用请求API 数据了,每个 API 请求都必须带有令牌。

具体做法:
方式一:
需要将令牌添加在请求头,key为Authorization,值为Bearer xxxtoken格式,再次访问,发现获取到了资源。
方式二:
在API请求跟上参数?access_token=xxxtoken

二、资源服务器

1、资源服务器配置

资源服务器配置比较简单,这里说明一下重要配置。

1.1 OAuth2授权配置类

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true) // 开启SpringSecurity权限注解
public class OauthSourceConfig extends ResourceServerConfigurerAdapter {@Autowiredprivate DataSource dataSource;/*** 指定token的持久化策略* InMemoryTokenStore表示将token存储在内存* Redis表示将token存储在redis中* JdbcTokenStore存储在数据库中* @return*/@Beanpublic TokenStore jdbcTokenStore(){return new JdbcTokenStore(dataSource);}/*** 指定当前资源的id和存储方案* @param resources* @throws Exception*/@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.resourceId("order_source_api")//资源ID.tokenStore(jdbcTokenStore());//令牌策略}@Overridepublic void configure(HttpSecurity http) throws Exception{//一般固定写法http.authorizeRequests()//指定不同请求方式访问资源所需要的权限,一般查询是read,其余是write。.antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')").antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')").antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')").antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')").antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')").and().headers().addHeaderWriter((request, response) -> {response.addHeader("Access-Control-Allow-Origin", "*");//允许跨域if (request.getMethod().equals("OPTIONS")) {//如果是跨域的预检请求,则原封不动向下传达请求头信息response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));}});}}

1.2 controller

@RestController
@RequestMapping("/order")
public class OrderController {@Secured({"ROLE_USER","ROLE_ORDER"})@RequestMapping(value = "/findOrder", method = {RequestMethod.GET, RequestMethod.POST})public String findOrder(){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println("SecurityContextHolder.getContext().getAuthentication() ->" + authentication.toString());return " findOrder成功!" + authentication.toString();}@Secured({"ROLE_ADMIN","ROLE_ORDER"})@RequestMapping(value = "/updateOrder", method = RequestMethod.POST)public String updateOrder(){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();System.out.println("SecurityContextHolder.getContext().getAuthentication() ->" + authentication.toString());return " updateOrder成功!" + authentication.toString();}}

2、通过令牌访问

携带令牌访问,令牌需要添加在请求头,key为Authorization,值为Bearer xxxtoken格式。

参考文章:

  • OAuth2的四种授权模式:https://www.cnblogs.com/Innocent-of-Dabber/p/11009811.html
  • SpringSecurity OAuth2 资源服Token反序列化解析失败:https://blog.csdn.net/CSDN877425287/article/details/120663052

– 求知若饥,虚心若愚。

Spring Security+Oauth2四种授权模式相关推荐

  1. 零基础学习SpringSecurity OAuth2 四种授权模式(理论+实战)(配套视频讲解)

    配套视频直达 背景 前段时间有同学私信我,让我讲下Oauth2授权模式,并且还强调是零基础的那种,我也不太理解这个零基础到底是什么程度,但是我觉得任何阶段的同学看完我这个视频,对OAuth2的理解将会 ...

  2. SpringBoot Security的oauth2四种授权模式使用

    oauth2四种使用方式 密码模式 localhost:8080/oauth/token?client_id=client_id_1&client_secret=123456&gran ...

  3. (一)、Spring Security OAuth2 五种授权方式介绍

    更多相关文章请见:Spring Security文章目录 1.简介 OAuth 2.0定义了五种授权方式. RFC规范链接 authorization_code:授权码类型,授权系统针对登录用户下发c ...

  4. Spring Security Oauth2 (四) 密码码模式 代码

    F:. └─java└─claroja│ Springsecurityoauth2DemoApplication.java│├─config│ AuthorizationServerConfig.ja ...

  5. Outh2协议有哪四种授权模式?

    文章目录 1. OAuth2介绍 2. OAuth2的四种授权模式 ①:授权码模式 ②:密码模式 ③:简化(隐式)模式 ④:客户端模式 3. token的携带方式 4. token的自动续期 refr ...

  6. Spring Security Oauth2 如何自定义授权获取token

    Oauth2的默认授权模式有四种: 授权码模式-authorization_code 密码模式-password 客户端模式-client_credentials 隐式授权模式-implicit 我们 ...

  7. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

  8. Spring Security OAuth2.0认证授权

    文章目录 1.基本概念 1.1.什么是认证 1.2 什么是会话 1.3什么是授权 1.4授权的数据模型 1.4 RBAC 1.4.1 基于角色的访问控制 2.基于Session的认证方式 3.整合案例 ...

  9. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 [Spring Security OAuth2.0认证授权一:框架搭建和认证测试] [Spring Security OAuth2.0认证授权二:搭建资源服务] [Spring Securi ...

最新文章

  1. canvas三角函数模拟水波效果
  2. 深度学习和目标检测系列教程 10-300:通过torch训练第一个Faster-RCNN模型
  3. objective-c 编写规范_Objective-c成员变量的定义方式?如何写才规范?
  4. VB 设置ListView中指定一行的背景颜色
  5. cmd 命令 之 dir 之 强化使用
  6. java定义一个二维数组
  7. fabric usage
  8. jQuery,Table表头固定插件chromatable存在的问题及解决办法
  9. 有线网络高可用项目实施方案(更新中)
  10. mysql单表查询怎么做_mysql单表查询
  11. VS2008 ---- VS2013各个版本下载地址
  12. 如何提升Javascript 基础
  13. Hit Refresh读书摘要
  14. Latex 中文Beamer模板
  15. python——正则表达式详解(二)
  16. 观海智能观海舆情大数据SaaS云平台
  17. 第一次参加pub的地面聚会
  18. 程序员必读书单1.0
  19. 企业对接钉钉流程(企业内部应用-H5)
  20. 联表查询求和的一些问题

热门文章

  1. Django项目实战(附源码免费下载)
  2. 在java 使什么居中的代码_java 居中的代码
  3. 人类的悲欢虽不相通,但情感分析模型读得懂
  4. Linux stat命令Blocks字段与IO Block字段的理解
  5. pytorch的max和argmax
  6. Linux 文件系统之 MINIX 文件系统
  7. dlopen 相关错误
  8. ImageNet介绍
  9. C语言:编程题(在某次比赛中,有10个评委给选手打分。现要求编程:1)输入一位选手的10个成绩;2)去掉一个最高分,一个最低分,求出平均成绩;3)输出该选手的平均成绩,保留一位小数。
  10. MacOS 安装 Homebrew