Почему при использовании аутентификации sso с использованием клиента пользователь не остается в системе на сервере (весенняя загрузка oauth2)?
Я создал серверное приложение и клиент, используя предоставление кода авторизации с помощью весенней загрузки. Я заметил, что когда я захожу через Facebook и открываю новую вкладку, а затем захожу на сайт Facebook, я уже вошел в систему (потому что я вошел в систему с помощью клиента, использующего их механизм аутентификации.
Я не могу добиться этого с моей конфигурацией клиент / сервер. Если я вхожу в систему, используя мой клиент (я перенаправлен на страницу входа на сервер, а затем перенаправлен обратно - вход в систему работает на клиенте), то при попытке открыть приложение сервера (на котором также есть некоторые страницы, доступные после аутентификации), я снова вижу страница авторизации. Я хотел бы иметь тот же поток, что и Facebook, чтобы быть уже аутентифицированным на серверном приложении.
Если я вхожу непосредственно в серверное приложение, то открываю другую вкладку с клиентом в chrome и пытаюсь выполнить некоторые действия над клиентским приложением. Я там больше не захожу.
Как я могу оставаться аутентифицированным в приложении сервера после аутентификации клиента?
Конфигурация сервера:
@Configuration
@RestController
@EnableOAuth2Client
@EnableAuthorizationServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityOauth2Configuration extends WebSecurityConfigurerAdapter {
@Autowired
OAuth2ClientContext oauth2ClientContext;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Autowired
UserDetailsService userDetailsService;
@Autowired
UserService userService;
@RequestMapping({"/user", "/me"})
public Map<String, Object> user(Principal principal, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("name", principal.getName());
try {
UserDTO userDTO = userService.findUserByEmail(principal.getName());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, "@type");
String value = objectMapper.writeValueAsString(userDTO);
map.put("userDTO", value);
} catch (IOException e) {
e.printStackTrace();
} catch (UserNotFoundException e) {
}
return map;
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/oauth/**", "/custom-logout","/logout", "/login**", "/webjars/**", "/rest/**")
.permitAll().anyRequest().authenticated().antMatchers("/revoke/**", "/resources/**","/static/**", "/admin/**", "/public/**", "/logout2").permitAll()
.and().logout().clearAuthentication(true).invalidateHttpSession(true).logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login").permitAll().and().exceptionHandling()
.accessDeniedHandler(customAccessDeniedHandler())
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).and().formLogin()
// .loginProcessingUrl("/login")
.defaultSuccessUrl("/loggedin")
// .successHandler(customSocialAuthenticationSuccessHandler())//.permitAll()
.failureHandler(customAuthFailureHandler())
// .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.and().csrf().disable()
.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(1209600).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// @formatter:on
}
@Autowired
DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.requestMatchers().antMatchers("/registration", "/abc", "/me", "/resources/**", "/static/**", "/revoke/**", "/rest/**", "/logout2",
"/public/**" //public information pages
)
.and().authorizeRequests().antMatchers("/me").access("#oauth2.hasScope('read')")
.antMatchers("/rest/**", "/admin/**").fullyAuthenticated()
;
// @formatter:on
}
}
@Bean
public CustomAuthFailureHandler customAuthFailureHandler() {
return new CustomAuthFailureHandler();
}
@Bean
public AccessDeniedHandler customAccessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean
@ConfigurationProperties("github")
public ClientResources github() {
return new ClientResources();
}
@Bean
@ConfigurationProperties("facebook")
public ClientResources facebook() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(facebook(), "/login/facebook"));
filters.add(ssoFilter(github(), "/login/github"));
filter.setFilters(filters);
return filter;
}
@Bean
public CustomAuthenticationSuccessHandler customSocialAuthenticationSuccessHandler(){
return new CustomAuthenticationSuccessHandler();
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
return filter;
}
}
class ClientResources {
@NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
@NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
// public static void main(String[] args) {
// SpringApplication.run(SocialApplication.class, args);
// }
class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(
HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException exc) throws IOException, ServletException {
Authentication auth
= SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
}
response.sendRedirect(request.getContextPath() + "/login?error=true&bla=userdoesnothavespecificrole");
}
}
class CustomAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler
{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
getRedirectStrategy().sendRedirect(request, response, "/login?error=true");
}
}
Конфигурация клиента
@RestController
@Configuration
@EnableOAuth2Client
public class ClientApplication extends WebSecurityConfigurerAdapter {
@Autowired
private OAuth2ClientContext oauth2ClientContext;
@Resource
@Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
@Autowired
private OAuth2RestTemplate restTemplate;
@Autowired
private SSOLogoutHandler mySsoLogoutHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
//first line : url accessible without auth
http.antMatcher("/**").authorizeRequests().antMatchers("/**", "/login**", "/*.bundle.js", "/*.woff**", "/*.ttf", "/resources/**", "/assets/**", "/user/**").permitAll()
.antMatchers("/rest/**").authenticated()/* .and().exceptionHandling() */
// .antMatchers("/api/**").authenticated()
.and().logout().logoutSuccessUrl("/").permitAll().addLogoutHandler(mySsoLogoutHandler).and().csrf()
.disable().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// @formatter:on
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Configuration
public class RestConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
@Bean
@ConfigurationProperties("security.oauth2")
public ClientResources gate() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(gate(), "/auth"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
OAuth2RestTemplate template = restTemplate;
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
return filter;
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate oAuth2RestTemplate() {
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(gate().getClient(), oauth2ClientContext);
oAuth2RestTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
return oAuth2RestTemplate;
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Qualifier(value = "publicRestAPI")
public RestTemplate publicRestTemplate() {
return new RestTemplate();
}
class ClientResources {
@NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
@NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
}
конфиг клиента yml:
server:
port: 9999
security:
oauth2:
client:
client-id: app-client-id
client-secret: app-client-secret
access-token-uri: http://127.0.0.1:8080/oauth/token
user-authorization-uri: http://127.0.0.1:8080/oauth/authorize
resource:
user-info-uri: http://127.0.0.1:8080/me
basic:
enabled: false
url.gate: http://127.0.0.1:8080/
url.gate.token.revoke: revoke/token
Конфигурация сервера iml:
security:
oauth2:
client:
client-id: app-client-id
client-secret: app-client-secret
scope: read,write
grant-type: password, authorization_code, refresh_token
auto-approve-scopes: '.*'
access-token-validity-seconds: 10
refresh-token-validity-seconds: 400000