I am using Spring Boot: v2.0.3.RELEASE and Spring Fox: v2.9.2.
Trying to access http://127.0.0.1:8080/swagger-ui.html results in:
Unable to infer base url. This is common when using dynamic servlet
registration or when the API is behind an API Gateway. The base url is
the root of where all the swagger resources are served. For e.g. if
the api is available at http://example.org/api/v2/api-docs then the
base url is http://example.org/api/. Please enter the location
manually:
Repeatedly clicking OK in the popup bar has no effect.
Here is my Spring Security Configuration:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
private SecurityProperties securityProperties;
private AppProperties AppProperties;
public WebSecurityConfig(UserDetailsService userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder,
SecurityProperties securityProperties,
AppProperties AppProperties) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.securityProperties = securityProperties;
this.AppProperties = AppProperties;
}
#Configuration
#Order(1)
public class BasicAuthenticationConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.csrf().disable()
.httpBasic().and()
.authorizeRequests().anyRequest().hasRole("ADMIN").and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(AppProperties.getAdminUsername())
.password(bCryptPasswordEncoder.encode(AppProperties.getAdminPassword()))
.roles("ADMIN");
}
}
#Configuration
#Order(2)
public class TokenAuthenticationConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/no-auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(securityProperties, authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/v2/api-docs", "**/swagger-resources/**", "/swagger-ui.html", "/webjars/**")
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.applyPermitDefaultValues();
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE"));
corsConfiguration.setMaxAge(3600L);
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
}
Here is the SpringFox configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Autowired
private AppProperties AppProperties;
#Bean
public WebMvcConfigurer mvcConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
};
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("foo-api")
.apiInfo(apiInfo())
.useDefaultResponseMessages(false)
.produces(Collections.singleton("application/json"))
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Foo API")
.termsOfServiceUrl(AppProperties.getApiTosUrl())
.version("1.0").build();
}
}
In my Spring-Security Configuration, I have tried to exclude all Spring-Fox paths as generally advised but it doesn't have any effect. What is going wrong?
Related
I am trying to update to Spring Boot 2.7.0-SNAPSHOT.
WebSecurityConfigurerAdapter is deprecated in this version.
Old WebSecurityConfig with WebSecurityConfigurerAdapter (working fine):
/**
* SecurityConfig
*
*/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
private OAuth2UserServiceImpl oAuth2UserServiceImpl;
/**
* for development
*
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
// for development
return NoOpPasswordEncoder.getInstance();
}
#Override
public void configure(WebSecurity web) {
// ignoring
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico", "/oauth2");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/login", "/error", "/message/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint("/login"));
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// OAuth2
http.oauth2Login().loginPage("/").defaultSuccessUrl("/home", false);
http.oauth2Login().userInfoEndpoint().userService(oAuth2UserServiceImpl);
http.oauth2Login().successHandler(oAuth2AuthenticationSuccessHandler);
http.oauth2Login().failureHandler(oAuth2AuthenticationFailureHandler);
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").deleteCookies("JSESSIONID");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.enableSessionUrlRewriting(false);
}
private MyAuthenticationFilter authenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/authentication", "POST"));
return filter;
}
private AuthenticationEntryPoint authenticationEntryPoint(String loginFormUrl) {
return new MyLoginUrlAuthenticationEntryPoint(loginFormUrl);
}
}
After reading this blog post, I have modified the new WebSecurityConfig:
/**
* SecurityConfig
*
*/
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
#Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
#Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Autowired
private OAuth2UserServiceImpl oAuth2UserServiceImpl;
/**
* for development
*
* #return
*/
#Bean
public PasswordEncoder passwordEncoder() {
// for development
return NoOpPasswordEncoder.getInstance();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers(
"/css/**", "/js/**", "/img/**", "/lib/**", "/favicon.ico", "/oauth2");
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/login", "/error", "/message/**").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint("/login"));
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
// OAuth2
http.oauth2Login().loginPage("/").defaultSuccessUrl("/home", false);
http.oauth2Login().userInfoEndpoint().userService(oAuth2UserServiceImpl);
http.oauth2Login().successHandler(oAuth2AuthenticationSuccessHandler);
http.oauth2Login().failureHandler(oAuth2AuthenticationFailureHandler);
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login").deleteCookies("JSESSIONID");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.enableSessionUrlRewriting(false);
return http.build();
}
private MyAuthenticationFilter authenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
// How can I fix this? ------------------------------------------
filter.setAuthenticationManager(authenticationManagerBean());
// --------------------------------------------------------------
filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/authentication", "POST"));
return filter;
}
private AuthenticationEntryPoint authenticationEntryPoint(String loginFormUrl) {
return new MyLoginUrlAuthenticationEntryPoint(loginFormUrl);
}
}
I was able to fix two methods.
(#configure(WebSecurity web) and #configure(HttpSecurity http))
However, I can't figure out how to fix authenticationManagerBean().
Where do I get the AuthenticationManager from?
Here's the entire class that took me day to configure. Hope it could save your time.
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class ApplicationSecurityConfig {
private final UserDetailsService userService;
private final AuthenticationConfiguration configuration;
#Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
AbstractAuthenticationProcessingFilter filter = new CustomizedAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/login").permitAll();
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(filter);
return http.build();
}
#Bean
AuthenticationManager authenticationManager() throws Exception {
return configuration.getAuthenticationManager();
}
#Autowired
void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
}
You can create a custom DSL. This is actually how Spring Security works internally.
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
#Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(new MyAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
Then you can apply the custom DSL when building the SecurityFilterChain:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.apply(customDsl());
return http.build();
}
If you want to inject AuthenticationManager into other spring components, you can use the following configuration.
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
This approach has solved the problem for me and you can inject AuthenticationManager wherever you need.
EDIT:
#EnableWebSecurity
#RequiredArgsConstructor
public class SpringSecurityConfiguration {
private final AuthenticationConfiguration authenticationConfiguration;
#Bean
public AuthenticationManager authenticationManager() throws Exception
{
return authenticationConfiguration.getAuthenticationManager();
}
I try to add a role hierarchy to my spring Security app I used this configuration
#Configuration
#ComponentScan (basePackages = "com.medkhelifi.security")
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
JwtTokenProvider jwtTokenProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AffirmativeBased getAccessDecisionManager() {
DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
expressionHandler.setDefaultRolePrefix("");
expressionHandler.setRoleHierarchy(roleHierarchy());
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(expressionHandler);
List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();
voters.add(webExpressionVoter);
return new AffirmativeBased(voters);
}
#Bean
public RoleHierarchyImpl roleHierarchy (){
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return roleHierarchy;
};
public JwtSecurityConfig(){
}
#Override
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.accessDecisionManager(getAccessDecisionManager())
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
//#formatter:on
}
}
The idea is to add ROLE_USER to ROLE_ADMIN hiearchy but it don't work when i try to apply a secured ROLE_USER to one of my request
#RequestMapping(method = RequestMethod.GET)
#Secured("ROLE_USER")
public String index(Principal principal){
return userRepository.findByEmail("admin#gmail.com").toString();
}
I get 403 Forbidden error, but when I replace #Secured("ROLE_USER") by #Secured("ROLE_ADMIN") it works very well.
I'm working on OAuth2 Authorization in Spring and try to implement authorization code grant flow. Now I have two applications. Client side and authorization server side. When I open secured /client/hello it redirect me to oauth2 login page, after that a get /oauth/authorize link, but in redirect_uri value always is login page on client side and it even doesn't change manually in browser. How I can change it? If i change redirect uri to /client/login in auth server config it redirects and gives me authorization code, but invokes unauthorized error.
Client
Controller:
#RestController
public class Controller {
#GetMapping("/hello")
public String hello() {
return "Hello world!!";
}
#GetMapping("/public")
public String publicPage() {
return "This is public!!";
}
#GetMapping("/callback")
public String login(#RequestParam("code") String code) {
return code;
}
}
Client security config:
#Configuration
#EnableOAuth2Sso
public class ClientSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error**", "/public**").permitAll()
.anyRequest().authenticated();
}
}
Client properties:
security.oauth2.client.client-id=007314
security.oauth2.client.client-secret=MDA3MzE0
security.oauth2.client.grant-type=password
security.oauth2.client.scope=read
security.oauth2.client.pre-established-redirect-uri=http://localhost:8081/client/public
security.oauth2.client.access-token-uri=http://localhost:8082/auth/oauth/token
security.oauth2.client.user-authorization-uri=http://localhost:8082/auth/oauth/authorize
security.oauth2.client.authentication-scheme=form
security.oauth2.resource.user-info-uri=http://localhost:8081/client/hello
security.oauth2.resource.id=resource-server-rest-api
server.port=8081
server.servlet.context-path=/client
Authorization Server
Server config:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
private final PasswordEncoder passwordEncoder;
#Qualifier("authenticationManagerBean")
private final AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServer(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager) {
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(passwordEncoder)
.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("007314")
.secret(passwordEncoder.encode("007314"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read")
.resourceIds("resource-server-rest-api")
.autoApprove(true)
.redirectUris("http://localhost:8081/client/hello");
}
#Bean
public TokenStore tokenStore(){
return new JwtTokenStore(defaultAccessTokenConverter());
}
#Bean
public JwtAccessTokenConverter defaultAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.accessTokenConverter(defaultAccessTokenConverter())
.authenticationManager(authenticationManager);
}
}
Server security config:
#EnableWebSecurity
#Order(1)
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())
.withUser("qwerty")
.password(passwordEncoder().encode("12345"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/error**", "/login**", "/oauth/authorize**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
Resource Server
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource-server-rest-api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/client/hello").access("#oauth2.hasScope('read')");
}
}
Server properties:
server.port=8082
server.servlet.context-path=/auth
Add also: security.oauth2.client.useCurrentUri=false into client.properties.
I am trying to implement Oauth2 with spring boot with the configurations as below
Securty configuration:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
auth.userDetailsService(userDetailsService);
}
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(11);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().anonymous().disable().authorizeRequests()
.antMatchers("/oauth2/login","/logout").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/oauth2/login").loginProcessingUrl("/login").permitAll();
}
}
Authorization configuration
#Configuration
#EnableAuthorizationServer
public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public TokenStore tokenStore(DataSource dataSource){
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore)
.tokenEnhancer(enhancerChain)
.tokenGranter(new CompositeTokenGranter(getCustomizedTokenGranters()))
.tokenServices(tokenServices())
.approvalStoreDisabled();
}
#Bean
#Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenEnhancer(tokenEnhancer());
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer(){
return (accessToken, authentication) -> {
if(!"client_credentials".equalsIgnoreCase(authentication.getOAuth2Request().getRequestParameters().get(OAuth2Utils.GRANT_TYPE)))
{
ExtendedUser principal = (ExtendedUser) authentication.getPrincipal();
Map<String, Object> additionalInfo = Maps.newHashMap();
additionalInfo.put("user_id", principal.getUserId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
};
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()")
.tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Bean
public ClientDetailsService clientDetailsService(DataSource dataSource){
return new CachedClientDetailsService(dataSource);
}
private List<TokenGranter> getCustomizedTokenGranters() {
AuthorizationServerTokenServices tokenServices = tokenServices();
ClientDetailsService clientDetails = clientDetailsService;
OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetails);
RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter(tokenServices, clientDetails, requestFactory);
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory);
clientCredentialsTokenGranter.setAllowRefresh(true);//custom config, see AuthorizationServerEndpointsConfigurer.getDefaultTokenGranters
List<TokenGranter> tokenGranters = Lists.newArrayList();
tokenGranters.add(refreshTokenGranter);
tokenGranters.add(implicit);
tokenGranters.add(clientCredentialsTokenGranter);
if (authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory));
}
return tokenGranters;
}
}
Resource server configuration :
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("identity-service");
}
#Bean
public ResourceServerTokenServices resourceServerTokenServices(TokenStore tokenStore){
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
return tokenServices;
}
}
application.properties
security.oauth2.resource.filter-order = 3
the resource server is on the same authorization server (Same Application), and I am trying to implement the Implicit grant (Password grant working just fine)
when I am trying to login in to complete the implicit grant (oauth/authorize endpoint need authentication) I am getting /login 404 ?
spring boot: 1.5.10,
spring security Oauth2: 2.0.14
Finally, i managed to make it work
Security Configuration :
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/**").and().csrf().disable().authorizeRequests()
.antMatchers("/oauth2/login","/logout").permitAll()
.antMatchers("/oauth/authorize").authenticated()
.and().formLogin().loginPage("/oauth2/login").loginProcessingUrl("/login").permitAll();
}
Resources Server Configuration
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**").and().anonymous().disable().authorizeRequests()
.anyRequest().authenticated();
}
I had to enable the anonymous in spring security and specify mapping URI matcher for both spring security and resource server
Resource server on /api/**
,Spring security on /**
and for taking care of the ordering (on version 1.5.10)
application.properties
security.oauth2.resource.filter-order = 3
I am trying to configure Spring Security and OAuth2 on java config. I am using Spring Security version 4.0.4.RELEASE and OAuth2 version 2.0.11.RELEASE.
Spring Security config works well. Also I can get an access token with OAuth2 AuthorizationServer, but my ResourceServer does not work correctly. When I set the annotation #EnableResourceServer I can only check my access token and other URLs I cannot open (Security configuration and AuthorizationServer configuration do not work). I see the following error:
<oauth>
<error_description>
An Authentication object was not found in the SecurityContext
</error_description>
<error>unauthorized</error>
</oauth>
If I remove an annotation #EnableResourceServer, my ResourceServer does not check an access token. It just redirects to the authentication page.
This is my code:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class GlobalSecurityConfig extends GlobalMethodSecurityConfiguration {
#Bean(name = "passwordEncoder")
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
#Qualifier("authUserDetailsService")
private UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Autowired
#Qualifier("permissionEvaluator")
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setDefaultRolePrefix("");
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return expressionHandler();
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean(name = "clientAuthenticationEntryPoint")
public OAuth2AuthenticationEntryPoint oauthAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entry = new OAuth2AuthenticationEntryPoint();
entry.setRealmName("myapp/client");
entry.setTypeName("Basic");
return entry;
}
#Autowired
#Qualifier("webExpressionHandler")
private DefaultWebSecurityExpressionHandler expressionHandler;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/html/**", "/webapi/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/admin/**", "/**")
.and()
.authorizeRequests()
.expressionHandler(expressionHandler)
.antMatchers("/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/1/admin/**").access("hasRole('ADMINISTRATOR')")
.antMatchers("/profile**").authenticated()
.antMatchers("/oauth/authorize").authenticated()
.and()
.formLogin().loginPage("/login")
.failureUrl("/login?error=1")
.loginProcessingUrl("/login-attempt")
.defaultSuccessUrl("/", false)
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied")
.and()
.csrf();
}
}
Oauth config:
#Configuration
public class Oauth {
#Configuration
#EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_oauth_server";
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.regexMatchers("/api/v0/.*").authenticated()
.antMatchers("/**").denyAll()
;
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private AuthorizationCodeServices verificationCodeService;
#Autowired
#Qualifier("clientDetails")
private ClientDetailsService clientDetailsService;
#Autowired
#Qualifier("tokenStore")
private TokenStore tokenStore;
#Bean(name = "tokenServices")
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
#Bean
public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception {
ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.authorizationCodeServices(verificationCodeService);
endpoints.tokenServices(tokenServices());
endpoints.reuseRefreshTokens(true);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()");
oauthServer.checkTokenAccess("permitAll()");
oauthServer.realm("myapp/client");
oauthServer.addTokenEndpointAuthenticationFilter(clientCredentialsTokenEndpointFilter());
oauthServer.allowFormAuthenticationForClients();
}
}
}
So, ResourceServer config overlap other configuration. How can I fix it? I would be thankful for any help.
I see that you want to protect some endpoints with an access token, and other endpoints with normal form login.
Can you try restricting the applicability of your ResourceServerConfiguration to apply only to certain endpoints by something like: http.requestMatcher(new AntPathRequestMatcher("/api/v0/**")).... Do the same for SecurityConfig but for the endpoints you want it to take care of.