package de.dominikschadow.javasecurity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.filter.CompositeFilter; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.WebUtils; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Requires the configuration of @code{github.client.clientId} and @code{github.client.clientSecret} as runtime parameter. * This project is based on the Spring Boot and OAuth2 tutorial available at https://spring.io/guides/tutorials/spring-boot-oauth2 */ @SpringBootApplication @EnableWebSecurity @RestController @EnableOAuth2Client @EnableAuthorizationServer @Order(6) public class SsoWithGitHubApplication extends WebSecurityConfigurerAdapter { @Autowired private OAuth2ClientContext oAuth2ClientContext; public static void main(String[] args) { SpringApplication.run(SsoWithGitHubApplication.class, args); } @RequestMapping({"/user"}) public Map<String, String> user(Principal principal) { Map<String, String> map = new LinkedHashMap<>(); map.put("name", principal.getName()); return map; } @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**", "/webjars/**").permitAll() .anyRequest().authenticated() .and().exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")) .and().logout().logoutSuccessUrl("/").permitAll() .and().csrf().csrfTokenRepository(csrfTokenRepository()) .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class) .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); // @formatter:on } @Bean public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(filter); registration.setOrder(-100); return registration; } @Bean @ConfigurationProperties("github") ClientResources github() { return new ClientResources(); } private Filter ssoFilter() { CompositeFilter filter = new CompositeFilter(); List<Filter> filters = new ArrayList<>(); filters.add(ssoFilter(github(), "/login/github")); filter.setFilters(filters); return filter; } private Filter ssoFilter(ClientResources client, String path) { OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(path); OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(client.getClient(), oAuth2ClientContext); oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate); UserInfoTokenServices tokenServices = new UserInfoTokenServices( client.getResource().getUserInfoUri(), client.getClient().getClientId()); tokenServices.setRestTemplate(oAuth2RestTemplate); oAuth2ClientAuthenticationFilter.setTokenServices(tokenServices); return oAuth2ClientAuthenticationFilter; } private Filter csrfHeaderFilter() { return new OncePerRequestFilter() { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); if (csrf != null) { Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN"); String token = csrf.getToken(); if (cookie == null || token != null && !token.equals(cookie.getValue())) { cookie = new Cookie("XSRF-TOKEN", token); cookie.setPath("/"); response.addCookie(cookie); } } filterChain.doFilter(request, response); } }; } private CsrfTokenRepository csrfTokenRepository() { HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository(); repository.setHeaderName("X-XSRF-TOKEN"); return repository; } class ClientResources { private OAuth2ProtectedResourceDetails client = new AuthorizationCodeResourceDetails(); private ResourceServerProperties resource = new ResourceServerProperties(); public OAuth2ProtectedResourceDetails getClient() { return client; } public ResourceServerProperties getResource() { return resource; } } }