Как связать некоторое состояние сеанса в авторизации OAuth2
Я пытаюсь обработать или "обернуть" приложение-службу RESTful API перед службой HTTP-сервера. Внутренний сервис поддерживает состояние аутентификации через проприетарные сеансы. Аутентификация с внутренним сервером достигается за счет запутанного разговора между аутентифицирующим клиентом и сервером. В целом, взаимодействие с этим сервером является нестандартным и недостаточно хорошим для представления нашим клиентам в виде API. Тем не менее, существует слишком много наследства, чтобы пересмотреть и переработать весь HTTP-интерфейс.
Моя цель состоит в том, чтобы абстрагировать интерфейс от этого внутреннего сервера, предоставив этому серверу RESTful-сервер, который реализует аутентификацию OAuth2 посредством предоставления пароля, чтобы сторонние API могли взаимодействовать с внутренним сервером стандартным и понятным способом. Я создал простое приложение REST SpringBoot, которое может аутентифицироваться с помощью бэкэнда. Часть, с которой я борюсь, - выяснить, как лучше всего сопоставить аутентифицированные токены / запросы с идентификаторами сеансов бэкэнда.
По сути, я пытаюсь связать немного состояния для каждой авторизованной авторизации. В качестве решения я подумал о реализации собственного TokenStore, который будет поддерживать отображение. В качестве альтернативы я подумал о реализации "Custom Token Granter". Однако у меня есть сильная интуиция, что есть лучший и более легкий способ достичь моей цели.
Дополнительная информация:
@EnableAuthorizationServer
а также @EnableResourceServer
находятся в одном приложении.
Информация о версии:
org.springframework.security.oauth:spring-security-oauth2
2.0.13.RELEASEorg.springframework.security:spring-security-config
4.2.2.RELEASEorg.springframework.boot:spring-boot-starter-security
1.5.3.RELEASE
Это моя конфигурация сервера ресурсов:
@Configuration
@Profile(Common.CONST_EXTAPI_PROFILE)
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and()
.headers().frameOptions().disable().and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/", "/home","/register","/login", "/auth").permitAll()
.antMatchers(HttpMethod.OPTIONS,"/oauth/token").permitAll()
.antMatchers("/extapi/agent", "/extapi/agent/**", "/extapi/account/**", "/extapi/sale/**").authenticated();
}
}
Это моя конфигурация сервера аутентификации:
@Configuration
@Profile(Common.CONST_EXTAPI_PROFILE)
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Order(-1)
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll();
}
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "foobar".toCharArray())
.getKeyPair("test");
converter.setKeyPair(keyPair);
return converter;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
clients.inMemory()
.withClient("acme")
.autoApprove(true)
.secret("acmesecret")
.authorizedGrantTypes("password", "authorization_code", "refresh_token").scopes("openid");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
"isAuthenticated()");
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
И пользовательский провайдер аутентификации
@Component
@Profile(Common.CONST_EXTAPI_PROFILE)
public class CustomAuthenticationProvider implements AuthenticationProvider
{
@Autowired
private LoginSessionData sessionData;
@Autowired
private GuiAuthenticationService authService;
@Autowired
private ContextService contextService;
@Autowired
private RSAEncryptionService rsaService;
@Autowired
private ObjectMapper mapper;
@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
final String username = auth.getName();
final String password = auth.getCredentials().toString();
Authentication result = null;
DataWrapper oauthResponse = new DataWrapper();
try
{
BaseResponse resp = authService.authenticate(contextService.getCompanyID(), false, null);
oauthResponse.setAuth(rsaService.getPublicKey());
oauthResponse.setData(mapper.writeValueAsString(resp));
if(resp.getState().equals("REQUIRE_UTF8_USERNAME")){
BaseRequest request = new BaseRequest();
request.setData(username);
request.setCid(Integer.toString(contextService.getCompanyID()));
sessionData.captureData(request);
resp = authService.process(request);
}
if(resp.getState().equals("REQUIRE_RSA_PASSWORD"))
{
BaseRequest request = new BaseRequest();
request.setData(password);
request.setCid(Integer.toString(contextService.getCompanyID()));
resp = authService.process(request);
}
if(resp.getState().equals("AUTHENTICATED"))
{
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(username, password, Collections.emptyList());
result = token;
}
}
catch (Exception e)
{
sessionData.setCurrentState(AuthenticationState.INVALID);
oauthResponse.setError(e.getLocalizedMessage());
oauthResponse.setData(Common.CONST_FATAL_ERROR);
e.printStackTrace();
}
if(result == null)
throw new BadCredentialsException("External system authentication failed");
return result;
}
@Override
public boolean supports(Class<?> auth) {
return auth.equals(UsernamePasswordAuthenticationToken.class);
}
}