创建一个Spring Security OAuth认证服务
理论
OAuth2是允许应用程序获取对HTTP服务(如GitHub、使用qq登录某网站、使用微信登录某网站等等)上的用户帐户的有限访问权限的授权框架。 它通过将用户身份验证委托给托管用户帐户的服务,并授权第三方应用程序访问用户帐户。 OAuth2为Web和桌面应用程序以及移动设备提供了授权流程。
一、什么是OAuth协议
OAuth(开放授权)是一个开放标准。允许第三方网站在用户授权的前提下访问在用户在服务商那里存储的各种信息。
而这种授权无需将用户提供用户名和密码提供给该第三方网站。
OAuth允许用户提供一个令牌给第三方网站,一个令牌对应一个特定的第三方网站,同时该令牌只能在特定的时间内访问特定的资源。
二、OAuth的原理和授权流程
OAuth的认证和授权的过程中涉及的三方包括:
服务商:
用户使用服务的提供方,一般用来存消息、储照片、视频、联系人、文件等(比如Twitter、Sina等)。
用 户:
服务商的用户
第三方:
通常是网站,该网站想要访问用户存储在服务商那里的信息。比如某个提供照片打印服务的网站,用户想在那里打印自己存在服务商那里的网络相册。在认证过程之前,第三方需要先向服务商申请第三方服务的唯一标识。
三、实例案例
基本上现在的互联网站都会提供开放授权。
以及公司内部的boss系统 统一账号登录 等等,都是类似的open auth标准。
四、OAuth认证和授权的过程
- 用户访问第三方网站网站,想对用户存放在服务商的某些资源进行操作。
- 第三方网站向服务商请求一个临时令牌。
- 服务商验证第三方网站的身份后,授予一个临时令牌。
- 第三方网站获得临时令牌后,将用户导向至服务商的授权页面请求用户授权,然后这个过程中将临时令牌和第三方网站的返回地址发送给服务商。
- 用户在服务商的授权页面上输入自己的用户名和密码,授权第三方网站访问所想要和能够访问的资源。
- 授权成功后,服务商将用户导向第三方网站的返回地址。
- 第三方网站根据临时令牌从服务商那里获取访问令牌。
- 服务商根据令牌和用户的授权情况授予第三方网站访问令牌。
- 第三方网站使用获取到的访问令牌访问存放在服务商对应的用户资源。
实战
一、代码
好,不说理论了,现在我们开始使用spring cloud oauth2创建一个认证服务吧。
新建项目:
引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Account
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
class Account {
public Account(String username, String password, boolean active) {
this.username = username;
this.password = password;
this.active = active;
}
@Id
@GeneratedValue
private Long id;
private String username, password;
private boolean active;
}
AccountRepository
interface AccountRepository extends JpaRepository<Account, Long> {
Optional<Account> findByUsername(String username);
}
AccountUserDetailsService
@Service
class AccountUserDetailsService implements UserDetailsService {
private final AccountRepository accountRepository;
public AccountUserDetailsService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return accountRepository.findByUsername(username)
.map(account -> new User(account.getUsername(),
account.getPassword(), account.isActive(), account.isActive(), account.isActive(), account.isActive(),
AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER")
))
.orElseThrow(() -> new UsernameNotFoundException("couldn't find the username " + username + "!"));
}
}
AuthServiceConfiguration
@Configuration
@EnableAuthorizationServer
class AuthServiceConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager ;
AuthServiceConfiguration(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("html5")
.secret("password")
.authorizedGrantTypes("password")
.scopes("openid");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager( this.authenticationManager);
}
}
AuthServiceApplication
@SpringBootApplication
@RestController
public class AuthServiceApplication {
@Bean
CommandLineRunner demo(AccountRepository accountRepository) {
return args ->
Stream.of("hezhuofan,spring", "zhangsan,cloud", "lisi,boot", "wangmazi,security")
.map(tpl -> tpl.split(","))
.forEach(tpl -> accountRepository.save(new Account(tpl[0], tpl[1], true)));
}
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
}
application.properties:
spring.application.name=auth-service
server.port=9191
server.contextPath=/uaa
security.sessions=if_required
二、模拟请求
使用Postman来模拟请求:
authorization:
headers:
token:
{
"access_token": "67692d8f-49d5-440e-a332-22353e440a5b",
"token_type": "bearer",
"expires_in": 43199,
"scope": "openid"
}
总结
在微服务中,你可以使用oauth认证流程来保护一些需要保护rest api。在公司内部的统一账户登录认证中,也可以通过oauth的方式为要想要接入登录验证的内部项目提供统一登录入口。在互联网上,你也构建自己oauth认证server向第三方应用提供经过用户授权的用户资料。就写这么多,oauth认证中的概念写得不是很详细,以后有机会再专门涉及。
附:Postman安装指南
Chrome浏览器 > Apps > WebStore > search Postman
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 一天一大 leet(最小路径和)难度:中等-Day20200723
- 一天一大 leet(二叉树的最大深度)难度:简单-Day20200728
- 生产者消费者模式的三种实现方式
- java中的阻塞队列
- java虚拟机
- 重构:保持Dockerfile整洁的5个技巧
- spring boot启动过程
- 如何构造jvm的堆溢出和栈溢出
- 一日一技:导入父文件夹中的模块并读取当前文件夹内的资源
- Matpotlib绘图遇到时间刻度就犯难?现在,一次性告诉你四种方法
- Windows NetLogon权限提升漏洞(CVE-2019-1424) 复现
- 源码解析:Git的第一个提交是什么样的?
- 终于搞懂,为什么 Java 的 main 方法必须是 public static void?
- Spring Boot 多版本更新,紧急修复 RFD 安全漏洞
- 【查找】折半查找/二分查找