SpringCloud搭建微服务之OAuth2认证和授权
OAuth2.0是一个标准的授权协议,实际上它是用户资源和第三方应用之间的一个中间层,把资源和第三方应用隔开,使得第三方应用无法直接访问资源,第三方应用要访问资源需要通过提供凭证获得OAuth2.0授权,从而起到保护资源的作用
1. 概述
OAuth2.0是一个标准的授权协议,实际上它是用户资源和第三方应用之间的一个中间层,把资源和第三方应用隔开,使得第三方应用无法直接访问资源,第三方应用要访问资源需要通过提供凭证获得OAuth2.0授权,从而起到保护资源的作用
1.1. OAuth2.0角色
OAuth2.0在认证和授权过程中,主要有四种角色
授权服务(Authorization Server):进行访问的认证和授权
资源服务(Resource Server):存放用户资源的服务
资源所有者(Resource Owner):用户
客户端(Client):与授权和资源服务提供无关的任何第三方应用
1.2. OAuth2.0运行流程
- 用户打开客户端,客户端要求用户给予授权
- 用户同意给客户端授权
- 客户端使用上一步获得的授权向授权服务器申请令牌
- 授权服务器对客户端进行认证,在确认无误后发放令牌
- 客户端使用令牌向资源服务器申请获取资源
- 资源服务器确认令牌正确后,向客户端开放资源
1.3. 客户端授权模式
OAuth2.0定义了以下4中授权模式
1.3.1. 密码模式
用户向客户端提供自己的用户名和密码,客户端使用这些信息向授权服务器申请授权
客户端发出HTTP请求参数如下:
grant_type:授权类型
username:用户名
password:用户密码
scope:权限范围
1.3.2. 客户端模式
客户端直接向授权服务器进行认证
HTTP请求参数如下:
grant_type:授权类型
scope:权限范围
1.3.3. 授权码模式
客户端通过后台服务器与授权服务器进行交互
客户端申请认证参数如下:
response_type:授权类型
client_id:客户端ID
redirect_uri:重定向URI
scope:权限范围
state:客户端当前状态
响应参数如下:
code:授权码,客户端只能使用一次
state:当前状态
客户端向授权服务器申请令牌参数如下:
grant_type:授权模式authorization_code
code:授权码
redirect_uri:重定向URI
client_id:客户端ID
授权服务器响应参数如下:
access_token:访问令牌
token_type:令牌类型
expires_in:过期时间,单位秒
refresh_token:更新令牌
scope:权限范围
更新令牌请求参数如下:
grant_type:授权模式
refresh_token:刷新令牌
scope:权限范围
1.3.4. 简化模式
客户端直接在浏览器中向授权服务器申请令牌
HTTP请求参数如下:
response_type:授权
client_id:客户端ID
redirect_uri:重定向URI
scope:权限范围
state:客户端当前状态
HTTP响应参数如下:
access_token:访问令牌
token_type:令牌类型
expires_in:过期时间,单位秒
scope:权限范围
state:当前状态
2. 搭建授权服务
2.1. 引入核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
2.2. 编写application.yml
server:
port: 8815
spring:
application:
name: cloud-oauth2-authorization-server
main:
allow-bean-definition-overriding: true
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
management:
endpoints:
web:
exposure:
include: refresh,health,info,env
logging:
level:
root: WARN
org.springframework.web: INFO
org.springframework.security: INFO
org.springframework.security.oauth2: INFO
2.3. 编写AuthorizationServerConfig
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
/**
* 配置令牌端点安全策略
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//oauth/token_key公开
security.tokenKeyAccess("permitAll()")
//oauth/check_token公开
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
/**
* 配置客户端
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//设置clientId
.withClient("client1")
//设置clientSecret
.secret(new BCryptPasswordEncoder().encode("123456"))
//设置资源ID
.resourceIds("resource1")
//设置授权模式
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
//设置权限
.scopes("all")
//取消自动授权
.autoApprove(false)
//重定向地址
.redirectUris("http://localhost:8816/authorized");
}
/**
* 配置令牌服务
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET);
}
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
//客户端详情服务
services.setClientDetailsService(clientDetailsService);
//允许令牌自动刷新
services.setSupportRefreshToken(true);
//令牌存储策略-内存
services.setTokenStore(tokenStore);
//使用jwt令牌
services.setTokenEnhancer(accessTokenConverter);
//令牌默认有效期2小时
services.setAccessTokenValiditySeconds(7200);
//刷新令牌默认有效期3天
services.setRefreshTokenValiditySeconds(259200);
return services;
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
2.4. 编写TokenConfig
@Configuration
public class TokenConfig {
private static final String SIGN_KEY = "oauth2";
@Bean
public TokenStore tokenStore() {
//return new InMemoryTokenStore();
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey(SIGN_KEY);
return accessTokenConverter;
}
}
2.5. 编写WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(proxyTargetClass = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManagerBean();
}
@Override
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(
User.withUsername("admin").password(passwordEncoder().encode("123456")).authorities("USER").build(),
User.withUsername("manager").password(passwordEncoder().encode("123456")).authorities("USER").build(),
User.withUsername("worker").password(passwordEncoder().encode("123456")).authorities("USER").build());
return userDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//关闭csrf跨域检查
.authorizeRequests()
.anyRequest()
.authenticated()//其他请求需要登录
.and()
.formLogin();//默认login页面登录
}
}
3. 搭建资源服务
3.1. 引入核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
3.2. 编写application.yml
server:
port: 8816
spring:
application:
name: cloud-oauth2-resource-server
main:
allow-bean-definition-overriding: true
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
management:
endpoints:
web:
exposure:
include: refresh,health,info,env
3.3. 编写ResourceServerConfig
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource1";
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID)
//使用远程服务验证令牌服务
.tokenServices(tokenServices())
//无状态模式
.stateless(true);
}
/**
* 配置安全策略
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//路径匹配规则
.antMatchers("/oauth2/**")
//匹配scope
.access("#oauth2.hasScope('all')")
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
/**
* 配置access_token远程验证策略
* @return
*/
private ResourceServerTokenServices tokenServices() {
RemoteTokenServices services = new RemoteTokenServices();
services.setCheckTokenEndpointUrl("http://localhost:8815/oauth/check_token");
services.setClientId("client1");
services.setClientSecret("123456");
return services;
}
}
3.4. 编写WebSecurityConfig
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/oauth2/**")
.hasAuthority("oauth2")
.anyRequest().authenticated();
}
}
3.5. 编写controller
@RestController
@RequestMapping("/oauth2")
public class OAuth2ResourceController {
@GetMapping("/resources")
public String[] getResources() {
return new String[] {"Resource1", "Resource2", "Resource3"};
}
}
4. 验证
依次启动cloud-oauth2-authorization-server和cloud-oauth2-resource-server微服务,在postman中发起post请求http://localhost:8815/oauth/token获取token
再次发起get请求http://localhost:8816/oauth2/resources,将上面获取的token带入请求头
后记
spring-cloud-starter-oauth2已经过时,需要了解新方案的小伙伴,请参阅SpringCloud搭建微服务之OAuth2.1认证和授权
更多推荐
所有评论(0)