Spring Security OAuth2认证授权示例详解
作者:peterwanghao 发布时间:2022-09-11 19:45:47
本文介绍了如何使用Spring Security OAuth2构建一个授权服务器来验证用户身份以提供access_token,并使用这个access_token来从资源服务器请求数据。
1.概述
OAuth2是一种授权方法,用于通过HTTP协议提供对受保护资源的访问。首先,OAuth2使第三方应用程序能够获得对HTTP服务的有限访问权限,然后通过资源所有者和HTTP服务之间的批准交互来让第三方应用程序代表资源所有者获取访问权限。
1.1 角色
OAuth定义了四个角色
资源所有者 - 应用程序的用户。
客户端 - 需要访问资源服务器上的用户数据的应用程序。
资源服务器 - 存储用户数据和http服务,可以将用户数据返回给经过身份验证的客户端。
授权服务器 - 负责验证用户的身份并提供授权令牌。资源服务器接受此令牌并验证您的身份。
OAuth2的交互过程:
1.2 访问令牌与刷新令牌
访问令牌代表一个向客户授权的字符串。令牌包含了由资源所有者授予的权限范围和访问持续时间,并由资源服务器和授权服务器强制执行。
授权服务器向客户端发出刷新令牌,用于在当前访问令牌失效或过期时获取新的访问令牌,或获取具有相同或更窄范围的其他访问令牌,新的访问令牌可能具有比资源所有者授权的更短的生命周期和更少的权限。根据授权服务器的判断,发布刷新令牌是可选的。
访问令牌的责任是在数据到期之前访问它。
刷新令牌的责任是在现有访问令牌过期时请求新的访问令牌。
2. OAuth2 - 授权服务器
要使用spring Security OAuth2模块创建授权服务器,我们需要使用注解@EnableAuthorizationServer并扩展AuthorizationServerConfigurerAdapter类。
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory().withClient("clientapp")
.secret(passwordEncoder.encode("654321"))
.authorizedGrantTypes("password", "authorization_code",
"refresh_token")
.authorities("READ_ONLY_CLIENT").scopes("read_user_info")
.resourceIds("oauth2-resource")
.redirectUris("http://localhost:8081/login")
.accessTokenValiditySeconds(5000)
.refreshTokenValiditySeconds(50000);
}
}
Spring Security OAuth2会公开了两个端点,用于检查令牌(/oauth/check_token和/oauth/token_key),这些端点默认受保护denyAll()。tokenKeyAccess()和checkTokenAccess()方法会打开这些端点以供使用。
ClientDetailsServiceConfigurer用于定义客户端详细的服务信息,它可以在内存中或在数据库中定义。
在本例中我们使用了内存实现。它具有以下重要属性:
clientId - (必需)客户端ID。
secret - (可信客户端所需)客户端密钥(可选)。
scope - 客户受限的范围。如果范围未定义或为空(默认值),则客户端不受范围限制。
authorizedGrantTypes - 授权客户端使用的授权类型。默认值为空。
authorities - 授予客户的权限(常规Spring Security权限)。
redirectUris - 将用户代理重定向到客户端的重定向端点。它必须是绝对URL。
3. OAuth2 - 资源服务器
要创建资源服务器组件,请使用@EnableResourceServer注解并扩展ResourceServerConfigurerAdapter类。
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/**").authenticated();
}
}
以上配置启用/api下所有端点的保护,但可以自由访问其他端点。
资源服务器还提供了一种对用户自己进行身份验证的机制。在大多数情况下,它通常是基于表单的登录。
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.requestMatchers()
.antMatchers("/oauth/authorize**", "/login**", "/error**")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("peterwanghao")
.password(passwordEncoder().encode("123456")).roles("USER");
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4. OAuth2保护的REST资源
本例中只创建了一个RESTful API,它返回登录用户的姓名和电子邮件。
@Controller
public class RestResource {
@RequestMapping("/api/users/me")
public ResponseEntity<UserInfo> profile() {
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String email = user.getUsername() + "@126.com";
UserInfo profile = new UserInfo();
profile.setName(user.getUsername());
profile.setEmail(email);
return ResponseEntity.ok(profile);
}
}
public class UserInfo {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [name=" + name + ", email=" + email + "]";
}
}
5.演示
我们有一个API http://localhost:8080/api/users/me ,我们想通过第三方应用程序来访问API需要OAuth2令牌。
5.1 从用户获取授权许可代码
如上面的序列图所示,第一步是从URL获取资源所有者的授权:
http://localhost:8080/oauth/authorize?client_id=clientapp&response_type=code&scope=read_user_info
通过浏览器访问上面的URL地址,它将展现一个登录页面。提供用户名和密码。对于此示例,请使用“peterwanghao”和“123456”。
登录后,您将被重定向到授予访问页面,您可以在其中选择授予对第三方应用程序的访问权限。
它会重定向到URL,如:http://localhost:8081/login?code=TUXuk9 。这里'TUXuk9'是第三方应用程序的授权代码。
5.2 从授权服务器获取访问令牌
现在,应用程序将使用授权码来获取访问令牌。在这里,我们需要提出以下请求。使用此处第一步中获得的代码。
curl -X POST --user clientapp:654321 http://localhost:8080/oauth/token -H "content-type: application/x-www-form-urlencoded" -d "code=TUXuk9&grant_type=authorization_code&redirect_uri=http://localhost:8081/login&scope=read_user_info"
访问令牌响应
{
"access_token": "168aad01-05dc-4446-9fba-fd7dbe8adb9e",
"token_type": "bearer",
"refresh_token": "34065175-1e92-4bb0-918c-a5a6ece1dc5f",
"expires_in": 4999,
"scope": "read_user_info"
}
5.3 从资源服务器访问用户数据
一旦我们有了访问令牌,我们就可以转到资源服务器来获取受保护的用户数据。
curl -X GET http://localhost:8080/api/users/me -H "authorization: Bearer 168aad01-05dc-4446-9fba-fd7dbe8adb9e"
获得资源响应
{
"name":"peterwanghao",
"email":"peterwanghao@126.com"
}
6.总结
本文讲解了OAuth2授权框架的实现机制,通过一个例子说明了第三方应用程序如何通过授权服务器的授权去资源服务器上获取受保护的数据。本例的完整代码在GitHub 上。
来源:https://blog.csdn.net/peterwanghao/article/details/99661055
猜你喜欢
- 归并排序原理1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。⒉将相邻的两个子组
- springboot + docker + jenkins自动化部署项目,jenkins、mysql、redis都是docker运行的,并且
- 在application.properties中填写中文信息,在读取该文件时会出现中文乱码问题。比如:application.propert
- 1、在POM.xml文件下添加如下代码;注意:version、configuration、executions三个标签是我后来查找添加的,网
- 关于UIToolbarToolBar工具栏是视图View的属性,可以在工具栏上添加工具栏按钮Bar Button Item(可以是自定义的C
- 本文实例为大家分享了Java实现简单邮件发送的具体代码,供大家参考,具体内容如下需要的jar包:activation-1.1.1.jarma
- 在开发中,用到springboot项目,当打包后部署运行时,出现了这个问题,网上搜了好多,又是加META-INF配置,又是加啥的,感觉spr
- startActivityForResult与startActivity的不同之处在于:1、startActivity( )仅仅是跳转到目标
- 首先,良好的编码规范非常重要。在 java 程序中,访问速度、资源紧张等问题的大部分原因,都是代码不规范造成的。单例的使用场景单例模式对于减
- 目录Spring事件驱动源码实战在项目实际开发过程中,我们有很多这样的业务场景:一个事务中处理完一个业务逻辑后需要跟着处理另外一个业务逻辑,
- 递归算法设计的基本思想是:对于一个复杂的问题,把原问题分解为若干个相对简单类同的子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递
- 介绍本文实现的功能有:1、播放音乐2、自定义流星数量、飞行速度、光晕大小、流星大小3、自定义表白话语 运用到的知识点有:GUI:j
- 这几天做项目,有些地方的图片需要用到圆形图片,所以百度了一下,在github上找到一个开源项目,处理很简单,效果如下:使用起来特别简单,一共
- Java裁剪压缩PNG图片,透明背景色变黑import java.awt.Graphics2D;import java.awt.Image;
- 最近经常在机房看同学在玩一个走迷宫的游戏,比较有趣,自己也用java写一个实现随机生成迷宫的算法,其实就是一个图的深度优先遍历算法.基本思想
- C#中的null与SQL中的NULL是不一样的,SQL中的NULL用C#表示出来就是DBNull.Value。注意:SQL参数是不能接受C#
- 一个比较常见的改进用户体验的方案是用Redo/Undo来取代确认对话框,由于这个功能比较常用,本文简单的给了一个在C#中通过Command模
- 这篇文章主要介绍了Spring如何在一个事务中开启另一个事务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 本章讲述:FileStream类的基本功能,以及简单示例;1、引用命名空间:using System.IO;2、注意:使用IO操作文件时,要
- 本文将介绍使用Spring Boot集成Mybatis并实现主从库分离的实现(同样适用于多数据源)。延续之前的Spring Boot 集成M