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.1构造方式一、直接构造String str = "fly";方式二 、调用构造方法进行构造对象S
- 本文实例讲述了C#实现在购物车系统中生成不重复订单号的方法。分享给大家供大家参考。具体分析如下:订单号在购物过程中起到了很好的识别作用,更方
- 我们先来看本地如何生成图片验证码的,再来写输出到网页的验证码如何实现。先来看最简单的—实现的功能是,将一个字符串变成图片写入到文件中实现代码
- @PropertySource作用是:对自定义的properties文件加载使用:@PropertySource(value={"
- 一、本地仓库初始化与远程仓库推送操作Idea 基本环境配置Github 配置Git 执行文件目录指定创建工程git02创建本地仓库并提交项目
- 简介: 很多软件为了安全防止恶意攻击,会在登录/注册时进行人机验证,常见的人机验证方式有:谷歌点击复选框进行验证,输入验证码验证,短信验证码
- 1、什么是const? 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(当然,我们可以偷梁
- springboot sqlSessionFactoryBean自定义1.新建一个配置类,加上configuration注解2.定制化Sql
- java异常分为两大类,Checked异常和Runtime异常,Checked异常都是在编译阶段可以被处理的异常。Checked异常和Run
- 目录1)在程序集中添加资源2)在程序集中查找资源这一篇单独拿出来分析这个程序集资源,为的就是不想让大家把程序集资源和exe程序强关联,因为程
- 废话不多说了额,直接给大家贴代码了,具体代码如下所示:/** * 下载指定路径的文件,并写入到指定的位置 * &
- 注入集合(数组、List、Map、Set)类型属性(1)创建类,定义数组,list,map,set类型属性,并且生成对应的set方法。(2)
- 既然是一个网关。那么全局过滤器肯定是少不了的一个存在。像是鉴权、认证啥的不可能每个服务都做一次,一般都是在网关处就搞定了。Zuul他就有很强
- 前言不知道从哪一个版本起,Android studio 设置界面中已经没有忽略文件的设置。可能也是没有找到。下面简单记录下如何简单高效的配置
- 本文实例讲述了Android开发之完成登陆界面的数据保存回显操作。分享给大家供大家参考,具体如下:LoginActivity.java:pa
- 一、使用线程的理由1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。2、可以使用线程来简化编码。3、可以使用线程来实现并发执行。二
- * 与过滤器在讲Spring boot之前,我们先了解一下过滤器和 * 。这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的
- 快速排序实现: namespace QuickSort { class QuickSort { public static void Sor
- Settings是WebView提供给上层App的一个配置Webview的接口,每个WebView都有一个WebSettings,要控制We