SpringSecurityOAuth专题学习-密码模式及客户端模式实例
本⽰例中涉及到的⼏个对象其关系如下图所⽰:
密码模式⼀般⽤于⽤户对客户端信任度最⾼的情况下,因为客户端需要保存⽤户在授权服务器中的⽤户名及密码信息,客户端可以访问所有⽤户资源,因此⼀般在公司内部应⽤之间使⽤的较多。⽐如⼀个公司前端⼀般有安卓应⽤、苹果应⽤、Web端等,这个时候⽤户通过客户端使⽤⽤户名与密码登录的时候,这些信息实际上是告诉了客户端了,客户端可以拿这些信息来做任何⽤户权限内的事情。
在密码模式中,其处理流程如下:
客户端在授权服务器端的注册;
⽤户登录客户端,输⼊⽤户名及密码(或以某种其它⽅式保存⽤户在授权服务器中的⽤户名与密码);
客户端访问授权服务器验证授权,并获取访问Token;
客户端在后续的每次访问中都带上Token进⾏访问。
资源服务器接收到请求后,调⽤授权服务器相关接⼝校验Token有效性;
Token有效时,进⾏实际的业务逻辑处理;
Token有效时,返回相应错误信息给客户端;
另外,资源服务器与授权服务器可以在⼀个应⽤中,也可以分开。本⽂主要讲述分开处理时的实现,关于集中式实现请参考Spring Security OAuth的官⽅⽰例。
1. 授权服务器
授权服务器需要完成以下事情:
管理客户端及其授权信息;
管理⽤户及其授权信息;
管理Token的⽣成及其存储;
管理Token的校验及校验Key;
通过定义继承⾃AuthorizationServerConfigurerAdapter的⼀个配置类,以及Spring Security的配置,可以完成以上处理。
先来看授权服务器的最终配置:
1.1 MVN
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
1.2 Spring Security配置
通过Spring Security来完成⽤户及密码加解密等配置。
@Configuration
@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {        auth.inMemoryAuthentication().withUser("test").password("test").roles("USER") .and().passwordEncoder(passwordEncoder());
}
@Bean
public static NoOpPasswordEncoder passwordEncoder(){
return(NoOpPasswordEncoder) Instance();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean()throws Exception { return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http)throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().permitAll();
}
}
其中⼏个地⽅需要注意:
必须要配置⼀个密码加密解密器,⽰例中使⽤了NoOpPasswordEncoder,但这种⽅式是不安全的,因此这个类已经过时。实际项⽬中注意修改成BCryptPasswordEncoder等实现类;
需要将AuthenticationManager注⼊到容器中,在进⾏OAuth2配置时需要使⽤到。
1.3 Spring Security OAuth2配置
OAuth2的配置通过继承AuthorizationServerConfigurerAdapter的配置类实现。
@Configuration
public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
@Resource
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer clients)throws Exception {
web端登录clients.inMemory()
.withClient("test")
.resourceIds("testResource")
.
authorizedGrantTypes("password")
.authorities("ROLE_CLIENT")
.scopes("read","write")
.secret("secret")
.redirectUris("localhost:8080");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security)throws Exception {
.checkTokenAccess("hasAuthority('ROLE_CLIENT')")
.allowFormAuthenticationForClients();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
说明如下:
客户端的注册:本⽂通过inMemory的⽅式在内存中注册客户端相关信息;实际项⽬中可以通过⼀些管理接⼝及界⾯动态实现客户端的注册;
获取Token权限控制:客户端需要通过/oauth/token获取Token,此时实际上是未进⾏登录的,如果不配置将会报未授权错误;因此需要配置成tokenKeyAccess(“permitAll()”)
校验Token权限控制:资源服务器如果需要调⽤授权服务器的/oauth/check_token接⼝校验token有效
性,那么需要配置
checkTokenAccess(“hasAuthority(‘ROLE_CLIENT’)”),注意到⾓⾊是
ROLE_CLIENT,可见这种情况下资源服务器也需要当成⼀个客户端来进⾏注册。
authenticationManager配置:需要通过endpoints.authenticationManager(authenticationManager)将Security中的
authenticationManager配置到Endpoints中,否则,在Spring Security中
配置的权限控制将不会在进⾏OAuth2相关权限控制的校验时⽣效。
1.4 授权服务器启⽤
最后注意需要使⽤EnableAuthorizationServer来启动授权服务器:
@SpringBootApplication
@EnableAuthorizationServer
public class AuthenticationServerApplication {
public static void main(String[] args){
SpringApplication.run(AuthenticationServerApplication.class, args);
}
}
2. 资源服务器配置
2.1 主配置
资源服务器MVN依赖与授权服务器基本⼀致。⽽配置则通过继承⾃ResourceServerConfigurerAdapter的配置类来实现:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled =true)
public class OAuth2Configurer extends ResourceServerConfigurerAdapter {
@Bean
public ResourceServerTokenServices tokenServices(){
RemoteTokenServices tokenServices =new RemoteTokenServices();
tokenServices.setClientId("test");
tokenServices.setClientSecret("secret");
tokenServices.setCheckTokenEndpointUrl("localhost:8080/oauth/check_token");
return tokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources)throws Exception {
.tokenServices(tokenServices());
}
}
主要完成以下配置:
TokenService配置:在不采⽤JWT的情况下,需要配置RemoteTokenServices来充当tokenServices,它主要完成Token的校验等⼯作。因此需要指定校验Token的授权服务器接⼝地址。
同时,由于在授权服务器中配置了/oauth/check_token需要客户端登录后才能访问,因此也需要配置客户端编号及Secret;在校验之前先进⾏登录;
最后通过ResourceServerSecurityConfigurer来配置需要访问的资源编号及使⽤的TokenServices;
2.2 启⽤
需要使⽤EnableResourceServer来启⽤资源服务器
@SpringBootApplication
@EnableResourceServer
@EnableWebSecurity
public class ResourceServerApplication {
public static void main(String[] args){
SpringApplication.run(ResourceServerApplication.class, args);
}
}
2.3 测试接⼝
@RestController
@RequestMapping("/test")
@PreAuthorize("hasRole('ROLE_USER')")
public class TestController {
@GetMapping
public String test(){
return"test";
}
}
3. 测试
3.1 main函数测试
ResourceOwnerPasswordResourceDetails details =new ResourceOwnerPasswordResourceDetails();
details.setId("testResource");
details.setClientId("test");
details.setClientSecret("secret");
details.setScope(Arrays.asList("read","write"));
details.setGrantType("password");
details.setAccessTokenUri("localhost:8080/oauth/token");
details.setUsername("test");
details.setPassword("test");
OAuth2RestTemplate restTemplate =new OAuth2RestTemplate(details);
AccessTokenProviderChain provider =new AccessTokenProviderChain(Collections.singletonList(new ResourceOwnerPasswordAccessTokenProvider())); restTemplate.setAccessTokenProvider(provider);
System.out.AccessToken());
ResponseEntity<String> responseEntity = ForEntity(
System.out.println(responseEntity);
说明:
通过ResourceOwnerPasswordResourceDetails及OAuth2RestTemplate可以完成密码模式的OAuth2接⼝调⽤;
在调⽤最终需要调⽤的接⼝前,通过AccessToken()可以获取到AccessToken;
最终调⽤的时候,restTemplate中会⾃动在报⽂头中带上authentication信息,不再需要⼿⼯处理。
3.2 通过curl测试
3.2.1 获取Token:
curl -X POST -d grant_type=password -d username=test -d password=test test:secret@localhost:8080/oauth/token
或者
curl -X POST -d id=test -d client_id=test -d client_secret=secret -d scope=read -d grant_type=passw
ord -d username=test -d password=test localhost :8080/oauth/token
返回结果如下:
{"access_token":"ba3b7fa9-f206-4868-abd7-24ba8e83bc1b","token_type":"bearer","expires_in":43199,"scope":"read write"}
3.2.2 使⽤获取的Token访问接⼝:
curl -X GET -H "Authorization:Bearerba3b7fa9-f206-4868-abd7-24ba8e83bc1b" localhost:8081/test
返回结果如下:
test

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。