Spring Security OAuth 2 with traditional Spring MVC - spring-security

Issues setting up spring-security-oauth2 (password grant type) for a traditional non-boot spring mvc app.
Able to retrieve access token (step 1).
Get a status 401 when using 'access token' to retrieve secured resource (see step 2).
Curl Commands:
Get access token:
curl -X POST --user clientapp:123456 http://localhost:8080/oauth/token -H "accept: application/json" -H "content-type: application/x-www-form-urlencoded" -d "grant_type=password&username=adolfo&password=123&scope=read_profile"
Response:
{"access_token":"50c7c311-c73a-4a32-bc7e-6801bc64bbe0","token_type":"bearer","expires_in":41545,"scope":"read_profile"}
Retrieve secured data with access token
curl -X GET http://localhost:8080/api/profile -H "authorization: Bearer 50c7c311-c73a-4a32-bc7e-6801bc64bbe0"
Response: Status 401
The request has not been applied because it lacks valid authentication credentials for the target resource.
Configurations
//Resource Server Config
#Configuration
#EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
//#formatter:off
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/api/**");
//#formatter:on
}
}
Authorization Server Config
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("clientapp")
.secret("123456")
//.autoApprove(true)
.redirectUris("http://localhost:9000/callback")
.authorizedGrantTypes("password")
.scopes("read_profile", "read_contacts");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
Security Config
Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/signin")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.failureUrl("/signin-error")
.permitAll();
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
//.passwordEncoder(passwordEncoder())
.withUser("adolfo")
.password("123")
.roles("USER");
}
}
//Controller
#Controller
public class UserController {
#RequestMapping("/api/profile")
public ResponseEntity<UserProfile> profile() {
String username = (String) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
String email = username + "#mailinator.com";
UserProfile profile = new UserProfile();
profile.setName(username);
profile.setEmail(email);
return ResponseEntity.ok(profile);
}
Init Class
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{ SecurityConfig.class, OAuth2AuthorizationServer.class, OAuth2ResourceServer.class,};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{DispatcherConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("springSecurityFilterChain");
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[] {delegatingFilterProxy,hiddenHttpMethodFilter};
}
}
//Logs
2018-08-13 15:50:29,333 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token_key']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token_key'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/check_token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/check_token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/api/**']
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/api/**'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:68] matched
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', GET]
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/logout'
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', POST]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'POST /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', PUT]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'PUT /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', DELETE]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'DELETE /logout
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
2018-08-13 15:50:29,347 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter [OAuth2AuthenticationProcessingFilter.java:165] Authentication request failed: error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.w.h.w.HstsHeaderWriter [HstsHeaderWriter.java:129] Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#5337f7a
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer [DefaultOAuth2ExceptionRenderer.java:101] Written [error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#734d0eb7]
2018-08-13 15:50:29,366 DEBUG [http-nio-8080-exec-10] o.s.s.w.c.SecurityContextPersistenceFilter [SecurityContextPersistenceFilter.java:119] SecurityContextHolder now cleared, as request processing completed
See the entire log here

I don't see the OAuth2AuthenticationProcessingFilter in your stacktrace. I think it is because of the #Order(2) annotation that is placed on the SecurityConfig class. Your web security configuration clashes with Spring's internal configuration (org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration). Try to remove #Order annotation from the SecurityConfig class.
EDIT
Another reason of getting 401 response is using different instances of org.springframework.security.oauth2.provider.token.TokenStore: one for storing of newly created tokens and another for authentication. It may be caused by the wrong configuration of servlet initializer when implementation of org.springframework.web.servlet.config.annotation.WebMvcConfigurer references (through ComponentScan annotation for example) to other configurations that are already declared as root configuration classes. It leads to creation of two application contexts that are clash with each other.
Let's suppose you have three configuration classes:
ServletConfig that implements org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
AuthorizationServerConfig;
ResourceServerConfig.
There are two ways to configure your servlet initializer. The first one is to return ServletConfig class from getServletConfigClasses method:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {ServletConfig.class};
}
}
By choosing this way you should make sure you do not include classes from root configuration into servlet configuration (do not run component scan from ServletConfig class against package where root configuration classes are located).
The second way is to move ServletConfig to root configuration classes returning null from getServletConfigClasses:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ServletConfig.class, AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
}
It is perfectly fine to choose the second way as long as you do not need multiple dispatcher servlets.

Related

How to whitelisting Ip Address in spring security 6 [duplicate]

I've read in Spring Security Reference that AuthorizationFilter supersedes FilterSecurityInterceptor. So I'm trying to migrate my application to this newer method.
I have something like
http.authorizeRequests()
.mvcMatchers("/")
.hasIpAddress("127.0.0.1")
According to the linked page I should be able to write something like
http.authorizeHttpRequests()
.mvcMatchers("/")
.access("hasIpAddress('127.0.0.1')")
but there's no access(String) method. I even tried to paste verbatim code from the documentation:
#Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
// ...
.authorizeHttpRequests(authorize -> authorize
.mvcMatchers("/resources/**", "/signup", "/about").permitAll()
.mvcMatchers("/admin/**").hasRole("ADMIN")
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().denyAll()
);
return http.build();
}
which does not compile for the same reason.
Here's compilation error:
Application.java:103:55
java: incompatible types: java.lang.String cannot be converted to org.springframework.security.authorization.AuthorizationManager<org.springframework.security.web.access.intercept.RequestAuthorizationContext>
How do I use authorizeHttpRequests with IP addresses or string expression? Is it issue with documentation?
I'm using Spring Boot 2.7.0 and Spring Security 5.7.1
This does appear to be an issue with the docs. There is not currently a built-in implementation providing the hasIpAddress(String) access check, but you can use the IpAddressMatcher class to implement an AuthorizationManager capable of performing it.
Here's an example configuration:
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorizeRequests) -> authorizeRequests
.mvcMatchers("/").access(hasIpAddress("127.0.0.1"))
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
}
private static AuthorizationManager<RequestAuthorizationContext> hasIpAddress(String ipAddress) {
IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(ipAddress);
return (authentication, context) -> {
HttpServletRequest request = context.getRequest();
return new AuthorizationDecision(ipAddressMatcher.matches(request));
};
}
}

Spring Security - Custom JWT error messages

I am building a microservice using Spring Cloud Gateway and OAuth2 Resource Server. The app aims at redirecting to other microservices after doing the security part. I am trying to setup a filter before AnonymousAuthenticationFilter and handle my custom exception from there but however the custom exception filter is never being invoked. Following the security config I have in the app:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable()
.httpBasic().disable()
.formLogin().disable()
.addFilterBefore(customExceptionHandler, AnonymousAuthenticationFilter.class)
.authorizeRequests( auth -> auth.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/**").authenticated())
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt())
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
}
In my customExceptionHandler, I have the following code:
public class CustomExceptionHandler extends OncePerRequestFilter {
#Autowired
#Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (Exception e) {
log.error("Spring Security Filter Chain Exception:", e);
resolver.resolveException(request, response, null, e);
}
}
}
Also following is my build.gradle:
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// Spring Cloud
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
I also have an #ExceptionAdvice class that handles all the exceptions. However, if I pass in an expired JWT or any other error scenario to the service, I always get handled by the following error message in my WWW-Authenticate header:
Bearer error="invalid_token", error_description="Jwt expired at 2022-06-16T19:58:09Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
How do I throw a custom POJO instead of this message?
This error is coming from BearerTokenAuthenticationEntryPoint, so to override the behavior you can just easily provide a custom entryPoint
.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt().and().authenticationEntryPoint(myCustomEntryPoint))

Altering URL for Spring Security SAML2 Login

I have an application with multiple authentication types (i.e. Basic and a special Preauthorized login). I am attempting to add a SAML2 RelyingParty registration in my security configuration, where I am attempting to change the default path from:
/login/saml2/sso/{registrationId}
to
/auth/saml2/{registrationId}
So, I have the following setup:
public RelyingPartyRegistration provder1RelyingPartyRegistration() {
RelyingPartyRegistration registration = RelyingPartyRegistrations
.fromMetadataLocation("classpath:provider1/metadata.xml")
.registrationId("provider1")
.assertionConsumerServiceLocation("{baseUrl}/auth/saml2/{registrationId}")
.build();
return registration;
}
// #Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
Collection<RelyingPartyRegistration> registrations = Collections.unmodifiableList(Arrays.asList(provider1RelyingPartyRegistration()));
InMemoryRelyingPartyRegistrationRepository repository = new InMemoryRelyingPartyRegistrationRepository(registrations);
return repository;
}
// fluff
#Override
protected void configure(HttpSecurity http) throws Exception {
final RequestMatcher filterRequestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher("/auth/basic"),
new AntPathRequestMatcher("/auth/preauth")
);
ApplicationAuthenticationProcessingFilter filter = new ApplicationAuthenticationProcessingFilter(filterRequestMatcher, authenticationManagerBean());
filter.setAuthenticationSuccessHandler(successHandler());
filter.setAuthenticationFailureHandler(failureHandler());
http
.authorizeRequests()
.antMatchers("/**").permitAll()
.and()
.addFilterAfter(filter, LogoutFilter.class)
// fluff
.and()
.saml2Login()
.relyingPartyRegistrationRepository(relyingPartyRegistrationRepository())
.loginProcessingUrl("/auth/saml2/{registrationId}")
;
}
Unfortunately, I get this:
14 Dec 10:55:34 WARN [https-openssl-nio-127.0.0.1-444-exec-2] (DispatcherServlet.java:1278) - No mapping for POST /svc/auth/saml2/provider1
Can anyone tell me what I'm doing wrong trying to change that path? My application does NOT use Spring Boot, so I'm stuck with manual configuration.
EDIT
Some debugging has led to this hitting this line in the Saml2LoginConfigurer:
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository);
Somehow, there's a default authenticationRequestEndpoint (since I didn't define one) setting the filterProcessingUrl to a value of /saml2/authenticate/{registrationId}. So, how do I override this?
The loginProcessingUrl is called by the asserting party after the authentication succeeds, which contains in the request the SAMLResponse parameter.
What you are trying to change is the URL to process an authentication request (create the SAMLRequest and send to the asserting party), this is done by the Saml2WebSsoAuthenticationRequestFilter class. To change the redirectMatcher you have to provide an ObjectPostProcessor, see this issue.
ObjectPostProcessor<Saml2WebSsoAuthenticationRequestFilter> processor = new ObjectPostProcessor<>() {
#Override
public <O extends Saml2WebSsoAuthenticationRequestFilter> O postProcess(O filter) {
filter.setRedirectMatcher(new AntPathRequestMatcher("/my/custom/url"));
return filter;
}
};
http.saml2Login().addObjectPostProcessor(processor);
Take a look at SAML 2.0 Login Overview for more detail about the flow.

DefaultSecurityFilterChain skipping the specifc antmatchers

While configuring Httpsecurity in server side, I have 4 endpoints and I configured them according to roles.
But somehow only some antmatchers are registered in the filters, while skipping the few.
Spring Security console o/p:
2019-07-25 12:10:41.546 INFO 1028 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/resources/**'], []
2019-07-25 12:11:18.323 INFO 1028 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#31ab75a5, org.springframework.security.web.context.SecurityContextPersistenceFilter#7a2ffb0b, org.springframework.security.web.header.HeaderWriterFilter#1af78e37, org.springframework.security.web.authentication.logout.LogoutFilter#3cd28235, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#36f80ceb, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#43b74979, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#7c751692, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#17541204, org.springframework.security.web.session.SessionManagementFilter#7d95a717, org.springframework.security.web.access.ExceptionTranslationFilter#37fd496a, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#5a5b394]
2019-07-25 12:11:52.761 INFO 1028 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/user/getEmployeesList/**']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#3777fc44, org.springframework.security.web.context.SecurityContextPersistenceFilter#6bb71265, org.springframework.security.web.header.HeaderWriterFilter#6d6d81c, org.springframework.security.web.authentication.logout.LogoutFilter#35b79b1f, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#7668f8fd, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#37816ea6, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#7a74672, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#79454d8e, org.springframework.security.web.session.SessionManagementFilter#7c59cf66, org.springframework.security.web.access.ExceptionTranslationFilter#67376bae, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#7eb94590]
2019-07-25 12:12:46.473 INFO 1028 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#4ed5492c, org.springframework.security.web.context.SecurityContextPersistenceFilter#65f96d58, org.springframework.security.web.header.HeaderWriterFilter#69ae7632, org.springframework.security.web.authentication.logout.LogoutFilter#19656e21, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#54eb2bf6, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#37d8562f, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter#6d01f2e5, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#48a61e38, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#52a3eef8, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#66e434e8, org.springframework.security.web.session.SessionManagementFilter#1bbfd42f, org.springframework.security.web.access.ExceptionTranslationFilter#69988d42, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#6167a456]
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/user/getOperatorList").hasAuthority("ROLE_OPERATOR")
.antMatchers("/operator/getOperator").hasAuthority("ROLE_OPERATOR")
.antMatchers("/user/getEmployeesList").hasAuthority("ROLE_ADMIN")
.antMatchers("/admin/getEmployeesList").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.permitAll().and().logout().permitAll();
http.csrf().disable();
}
Fixed this.
I need to add antmatchers for paths provided above in resourceserverconfiguration.
This Fixed my Solution.
#EnableResourceServer
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/admin/getEmployeesList")
.and()
.authorizeRequests()
.antMatchers("/admin/getEmployeesList").access("hasRole('ADMIN').and()
.requestMatchers()
.antMatchers("/user/getOperatorList","/user/coaches", "/user/players", "/user/schools")
.and()
.authorizeRequests()")
.antMatchers("/user/getOperatorList").access("hasRole('OPERATOR')")
.antMatchers("/user/coaches").access("hasRole('COACH')")
.antMatchers("/user/players").access("hasRole('PLAYER')")
.antMatchers("/user/schools").access("hasRole('SCHOOL')")
.and()
.authorizeRequests()
.anyRequest()
.access("#oauth2.hasScope('read')");
}

What login name to use for Spring LDAP authentication

I created a local LDAP server and added the user "djiao" with password "123456
Trying to implement authentication with Spring Security with Spring Boot. My webconfig class is as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("", "ldap://localhost:10389");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
#Bean
public LoggerListener loggerListener() {
return new LoggerListener();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
However I can't seem to login from the login page.
If I use djiao (cn) or djiao1 (uid), I will get 500.
[LDAP: error code 34 - Incorrect DN given : djiao1 (0x64 0x6A 0x69 0x61 0x6F 0x31 ) is invalid]; nested exception is javax.naming.InvalidNameException: [LDAP: error code 34 - Incorrect DN given : djiao1 (0x64 0x6A 0x69 0x61 0x6F 0x31 ) is invalid]
If I use dn "cn=djiao,ou=Users,dc=example,dc=com" as the username I will get "Bad credentials" error. And the password is simply 123456.
What should the username for login? Or am I missing something in websecurityconfig class?
Since from your code I could identify that you're using Spring-Boot.
This is what was working for us connecting to LDAP
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder
.ldapAuthentication()
.userSearchFilter("(sAMAccountName={0})")
.userSearchBase("dc=some,dc=domain,dc=com")
.groupSearchBase("ou=groups,dc=some,dc=domain,dc=com")
.groupSearchFilter("member={0}")
.contextSource()
.url("ldaps://<ldap-server>")
.port(639)
.managerDn("cn=binduser,ou=users,dc=some,dc=domain,dc=com")
.managerPassword("some pass")
;
}
}
So in essence going for the userSearchFilter you'd have to define different values. If you use any LDAP besides AD your filter should by "(uid={0})" or if you wan't people to be able to use the email you could also go for "(mail={0})" or a combination like "(|(uid={0})(mail={0}))" which woul allow to use both.
If you go for ActiveDirectory – which I assume you do not based on what you have written above – it should be the sAMAccountName as stated above to allow people to just enter their ID in the domain like MYDOMAIN\myusername so the login would just be myusername.
If you need to connect to multiple LDAP-Server who share the same information for HA purposes you can do this through the .contextSource().url() call. If they carry different ones, e.g. 'EMEA', 'US', 'AP' you can combine these calls using:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
authBuilder
.ldapAuthentication()
.userSearchFilter("(sAMAccountName={0})")
.userSearchBase("dc=emea,dc=domain,dc=com")
.groupSearchBase("ou=groups,dc=emea,dc=domain,dc=com")
.groupSearchFilter("member={0}")
.contextSource()
.url("ldaps://<emea-ldap-server>")
.port(639)
.managerDn("cn=binduser,ou=users,dc=emea,dc=domain,dc=com")
.managerPassword("some pass")
.and()
.and()
.ldapAuthentication()
.userSearchFilter("(sAMAccountName={0})")
.userSearchBase("dc=ap,dc=domain,dc=com")
.groupSearchBase("ou=groups,dc=ap,dc=domain,dc=com")
.groupSearchFilter("member={0}")
.contextSource()
.url("ldaps://<ap-ldap-server>")
.port(639)
.managerDn("cn=binduser,ou=users,dc=ap,dc=domain,dc=com")
.managerPassword("some pass")
;
}
BTW: this also allows you to combine different authentication mechanisms like InMemory (Default-Admin-Backdoor) with LDAP and/or JDBC.

Resources