Я использую Spring Boot 2.0.3.
Я хочу использовать несколько методов аутентификации и настройки сервера ресурсов.
В настоящее время у меня есть:
- один микросервис аутентификации / авторизации, который по сути является хранилищем токенов (и имеет доступ к базе данных реальных пользователей)
- один сервер ресурсов, настроенный для проверки JWT в хранилище токенов
(см. Конфигурационные файлы ниже)
Все хорошо работает с JWT, но теперь я хочу предоставить пользовательские методы аутентификации, которые используют заголовки (один похож (но не) на базовую аутентификацию, а другой - с ключом / секретом API).
Мои исследования привели меня к определенным выводам, но, поскольку я довольно плохо знаком с безопасностью Spring, мне все еще неясно, какая глобальная архитектура, особенно с сервером авторизации.
Для меня будет иметь смысл, что любой запрос, поступающий на мой сервер ресурсов, будет проверяться на сервере авторизации, который будет возвращать авторизацию (аутентификацию и авторизацию).
Это будет означать, что мой сервер ресурсов должен отправить некоторые заголовки серверу авторизации и получить обратно авторизацию в случае, если JWT не предоставлен, так что автоматические проверки безопасности Spring выполнены правильно (роль и т. Д.), И я также вернусь ссылка пользователя как-то. И это должно быть сделано с надлежащей весенней модой, сохраняя при этом аутентификацию JWT.
Я видел:
Мои вопросы:
Какой весенний способ добиться этого?
файлы конфигурации
Аутентификационный сервер
@Configuration
@EnableAuthorizationServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
private static final Logger log = Logger.getLogger(OAuth2Configuration.class.getName());
@Value("${check-user-scopes}")
private Boolean checkUserScopes;
@Autowired
private DataSource dataSource;
@Bean
public PasswordEncoder passwordEncoder() {
return new LegacyPasswordEncoder();
}
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private ClientDetailsService clientDetailsService;
@Qualifier("authenticationManagerBean")
@Autowired
protected AuthenticationManager authenticationManager;
@Bean
public OAuth2RequestFactory requestFactory() {
CustomOauth2RequestFactory requestFactory = new CustomOauth2RequestFactory(clientDetailsService);
requestFactory.setCheckUserScopes(true);
return requestFactory;
}
@Bean
public TokenStore tokenStore() {
return new MyTokenStore(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder());
}
@Bean
public TokenEndpointAuthenticationFilter tokenEndpointAuthenticationFilter() {
return new TokenEndpointAuthenticationFilter(authenticationManager, requestFactory());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenServices(tokenServices()).authenticationManager(authenticationManager).userDetailsService(userDetailsService);
endpoints.exceptionTranslator(exception -> {
if (exception instanceof OAuth2Exception) {
OAuth2Exception oAuth2Exception = (OAuth2Exception) exception;
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(new CustomOauthException(oAuth2Exception.getMessage()));
} else {
throw exception;
}
});
if (checkUserScopes)
endpoints.requestFactory(requestFactory());
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenService = new DefaultTokenServices();
tokenService.setTokenStore(tokenStore());
tokenService.setSupportRefreshToken(true);
tokenService.setClientDetailsService(clientDetailsService);
tokenService.setTokenEnhancer(jwtAccessTokenConverter());
tokenService.setReuseRefreshToken(false);
return tokenService;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new CustomTokenEnhancer();
return converter;
}
/*
* Add custom user principal information to the JWT token
*/
class CustomTokenEnhancer extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put("username", user.getUsername());
info.put("email", user.getEmail());
info.put("roles", user.getRoles());
info.put("uuid", user.getUuid());
info.put("device", user.getDevice());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
return super.enhance(customAccessToken, authentication);
}
}
class CustomOauth2RequestFactory extends DefaultOAuth2RequestFactory {
@Autowired
private TokenStore tokenStore;
public CustomOauth2RequestFactory(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
}
@Override
public TokenRequest createTokenRequest(Map<String, String> requestParameters,
ClientDetails authenticatedClient) {
if (requestParameters.get("grant_type").equals("refresh_token")) {
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(
tokenStore.readRefreshToken(requestParameters.get("refresh_token")));
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(authentication.getName(), null,
userDetailsService.loadUserByUsername(authentication.getName()).getAuthorities()));
}
return super.createTokenRequest(requestParameters, authenticatedClient);
}
}
}
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
@Autowired
private CustomUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new LegacyPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling().and().cors();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
Ресурсный сервер
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile("!test")
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Value("${auth-server.url}")
private String authEndpoint;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Value("${security.oauth2.client.client-secret}")
private String clientSecret;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().cors().disable().csrf().disable().httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.accessDeniedHandler(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED));
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("ms/legacy");
}
@Bean
public ResourceServerTokenServices tokenService() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setClientId(clientId);
tokenServices.setClientSecret(clientSecret);
tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
return tokenServices;
}
}