Spring Cloud Security示例
下面是一个使用Spring Cloud Security的示例,该示例包括一个授权服务器和两个客户端,客户端之间通过授权服务器进行安全通信。
(1)授权服务器
授权服务器使用Spring Security OAuth2实现。在pom.xml文件中添加以下依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.1</version>
</dependency>
在授权服务器的配置文件中,配置OAuth2的客户端和提供者信息:
代码语言:javascript复制spring:
security:
oauth2:
client:
registration:
my-client-1:
client-id: my-client-1
client-secret: my-secret-1
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope:
- read
- write
- trust
my-client-2:
client-id: my-client-2
client-secret: my-secret-2
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope:
- read
- write
- trust
provider:
my-provider:
authorization-uri: https://provider.com/oauth2/authorize
token-uri: https://provider.com/oauth2/token
user-info-uri: https://provider.com/oauth2/userinfo
user-name-attribute: name
然后,创建一个授权服务器配置类,配置OAuth2的授权服务器和令牌存储:
代码语言:javascript复制@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
(2)客户端
客户端使用Spring Security OAuth2实现。在pom.xml文件中添加以下依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.4.1</version>
</dependency>
然后,创建一个配置类,配置OAuth2的客户端和令牌存储:
代码语言:javascript复制@Configuration
@EnableOAuth2Client
public class OAuth2ClientConfig {
@Value("${oauth2.client.registration.my-client-1.client-id}")
private String clientId;
@Value("${oauth2.client.registration.my-client-1.client-secret}")
private String clientSecret;
@Value("${oauth2.client.provider.my-provider.token-uri}")
private String accessTokenUri;
@Value("${oauth2.client.provider.my-provider.authorization-uri}")
private String userAuthorizationUri;
@Value("${oauth2.client.provider.my-provider.user-info-uri}")
private String userInfoUri;
@Value("${oauth2.client.provider.my-provider.user-name-attribute}")
private String usernameAttribute;
@Autowired
private OAuth2ClientContext oauth2ClientContext;
@Bean
public OAuth2RestTemplate oauth2RestTemplate() {
OAuth2ProtectedResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
resourceDetails.setClientId(clientId);
resourceDetails.setClientSecret(clientSecret);
resourceDetails.setAccessTokenUri(accessTokenUri);
resourceDetails.setUserAuthorizationUri(userAuthorizationUri);
resourceDetails.setUserInfoUri(userInfoUri);
resourceDetails.setPreEstablishedRedirectUri("http://localhost:8080/login");
resourceDetails.setUseCurrentUri(false);
resourceDetails.setClientAuthenticationScheme(AuthenticationScheme.form);
resourceDetails.setAuthenticationScheme(AuthenticationScheme.query);
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, oauth2ClientContext);
restTemplate.setMessageConverters(Arrays.asList(new MappingJackson2HttpMessageConverter()));
return restTemplate;
}
}
(3)安全通信
在客户端的REST API中,使用@PreAuthorize注解限制访问范围:
代码语言:javascript复制@RestController
public class MyRestController {
@Autowired
private OAuth2RestTemplate oauth2RestTemplate;
@GetMapping("/resource")
@PreAuthorize("#oauth2.hasScope('read')")
public String getResource() {
return oauth2RestTemplate.getForObject("https://provider.com/api/resource", String.class);
}
}
在这个示例中,客户端调用/provider/api/resource接口获取资源,但只有在令牌的访问范围包括read时才能成功调用。