package de.asideas.crowdsource.config; import de.asideas.crowdsource.security.IPBasedAnonymousAuthenticationFilter; import de.asideas.crowdsource.security.MongoUserDetailsService; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.AccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import java.io.IOException; /** * Configuration for Spring Security */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) @Import({SecurityConfig.ResourceServer.class, SecurityConfig.OAuth2Config.class}) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MongoUserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/app/**"); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Configuration @EnableResourceServer protected static class ResourceServer extends ResourceServerConfigurerAdapter { @Autowired private IPBasedAnonymousAuthenticationFilter ipBasedAnonymousAuthenticationFilter; @Override public void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) // at least one location of the app needs to be secured here, or the app won't start up, // even if there are controllers secured with the help of @EnableGlobalMethodSecurity // -> we define some dummy value to hopefully never match a real url // (haven't found the right way to configure spring security yet ...) .and().authorizeRequests().antMatchers("/some-pattern-to-make-spring-security-happy").authenticated() .and().anonymous().authenticationFilter(ipBasedAnonymousAuthenticationFilter); } } @Configuration @EnableAuthorizationServer protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Bean public AccessTokenConverter accessTokenConverter() throws IOException { final JwtAccessTokenConverter jwtTokenEnhancer = new JwtAccessTokenConverter(); jwtTokenEnhancer.setSigningKey(IOUtils.toString(getClass().getResourceAsStream("/security/tokensigningkey"))); jwtTokenEnhancer.setVerifierKey(IOUtils.toString(getClass().getResourceAsStream("/security/tokensigningkey.pub"))); return jwtTokenEnhancer; } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { // sets up a filter that listens on /oauth/token to let clients request a token oauthServer.allowFormAuthenticationForClients(); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .accessTokenConverter(accessTokenConverter()); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("web") .authorizedGrantTypes("password", "refresh_token") // token valid one year (we do not use the refresh token yet) .accessTokenValiditySeconds(60 * 60 * 24 * 365) // at least one scope must be configured. A client could request authorization only for certain parts of the app. // We don't need it, so just define some dummy value .scopes("default"); } } }