什么是Oauth2
OAuth 2 是一种授权框架,允许第三方应用通过用户授权的形式访问服务中的用户信息,最常见的场景是授权登录;再复杂一点的比如第三方应用通过 Github 给开发者提供的接口访问权限内的用户信息或仓库信息。OAuth2 广泛应用于 web 、桌面应用、移动 APP 的第三方服务提供了授权验证机制,以此实现不同应用间的数据访问权限。 下面分别从不同角色、授权类型、使用场景及流程的纬度详细介绍 OAuth2
OAuth Roles
OAuth 定义了四种角色:
- 资源拥有者 (Resource Owner)
- 客户端 (Client)
- 资源服务器 (Resource Server)
- 授权服务器 (Authorization Server)
授权流程
接下来文中提到的客户端均为第三方应用,服务端均为被接入的服务。譬如很多网站有微信登录功能,那么这个网站就是客户端,微信就是服务端。
流程图解释:
- 用户点击客户端提供的授权请求
- 客户端请求服务的授权页面呈现给用户,用户点击确认授权后服务端返回授权许可凭证给客户端
- 客户端通过步骤二接收到的授权许可凭证及在服务端注册的应用信息请求服务端
- 如果步骤三验证通过服务端则返回 access token给客户端
- 客户端通过第四步获取的 access token 请求服务端获取资源
- 如果服务端校验 access token 成功,则返回指定资源给客户端
授权模式
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
构建一个资源服务器和认证服务器
相关依赖如下,springboot版本使用的是2.2.5稳定版,springcloud版本是Greenwich.SR2版本,先介绍授权码模式
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
构建资源服务器配置类
@Configuration
@EnableResourceServer// 启动资源服务器
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()// 对相关请求进行授权
.anyRequest()// 任意请求
.authenticated()// 都要经过验证
.and()
.requestMatchers()// 设置要保护的资源
.antMatchers("/user/**");// 保护的资源
}
}
这里是保护的资源
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication){
return authentication.getPrincipal();
}
}
构建认证服务器配置类
首先得配置好当前程序的SecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Lazy
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**","/login/**","/logout/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf()
.disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean("passwordEncoder")
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
相关的UserDetailsService的实现类,这里为了方便一点没有去连接数据库,不太会使用SpringSecurity的可以去查看SpringBoot+SpringSecurity整合(一)相关的合集
@Service("userService")
public class UserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String encode = passwordEncoder.encode("123456");
return new User(username,encode, AuthorityUtils.createAuthorityList("admin"));
}
}
配置相关的认证服务器的配置类,一般授权服务器和资源服务器不放在一起这里为了方便放在一起去使用
@Configuration
@EnableAuthorizationServer// 启动授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Lazy
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
// 授权服务的名字
.withClient("clients")
// 授权码
.secret(passwordEncoder.encode("112233"))
// 重定向位置
.redirectUris("http://www.baidu.com")
// 授权范围
.scopes("all")
/**
* authorization_code 授权码模式
*/
.authorizedGrantTypes("authorization_code");
}
}
启动项目
启动项目之后在浏览器中输入
http://localhost:8080/oauth/authorize?response_type=code&client_id=clients&redirect_uri=http://www.baidu.com&scope=all
连接地址,然后授权服务器会返回当前页面先输入登录账号和密码登录到系统中
然后当前服务器代表我们去访问授权服务器,返回让我们授权的页面选择Approve进行授权,
从百度的url地址上获取到验证码code
通过授权之后服务器再次通过授权码去访问认证服务器去获取access_token,这一个步骤我们通过postMan来完成因为是post请求,
localhost:8081/oauth/token
相关参数如下:
参数名 | 参数值 |
---|---|
grant_type | authorization_code(授权码模式) |
client_id | clients(客户端id) |
redirect_uri | http://www.baidu.com |
code | 从百度的url地址上获取的code |
scope | all |
通过send发送请求获取到了access_token的值这样就可以拿着这个token去获取到对应的值了
这样就可以获取到相关的值了,红色区域填相关的access_token值