Spring Boot - How to disable Keycloak? - spring

I have a Spring Boot project with keycloak integrated. Now I want to disable keycloak for testing purposes.
I tried by adding keycloak.enabled=false to application.properties as mentioned in Keycloak documentation but it didnt work.
So how do I disable it?

Update 2022
Please follow this excellent guide on Baeldung.
For anyone who might have the same trouble, here is what I did.
I didn't disable Keycloak but I made a separate a Keycloak config file for testing purposes.
Here is my config file
#Profile("test")
#Configuration
#EnableWebSecurity
public class SecurityTestConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll();
http.headers().frameOptions().disable();
http.csrf().disable();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public AccessToken accessToken() {
AccessToken accessToken = new AccessToken();
accessToken.setSubject("abc");
accessToken.setName("Tester");
return accessToken;
}
}
Please note it is important to use this only in a test environment and therefore I have annotated the config as #Profile("test"). I have also added an AccessToken bean since some of the auditing features in my application depend on it.

It should work, but based on the last comment on the jira ticket for this, it seems it is not.
As the description state you could exclude the spring boot autoconfiguration for keycloak adding to your application.properties: spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration

You need to exclude keycloak auto configuration. In order to do this just add this entry to your related spring configuration file, in your case application.properties.
spring.autoconfigure.exclude = org.keycloak.adapters.springboot.KeycloakAutoConfiguration

My Workaround:
1. Create a Custom-Filter and add it to the (Spring) Security-Chain in early position.
2. Create a flag in the application.yml (securityEnabled)
3. Query the flag in the Custom-Filter. If 'true' simply go on with the next filter by calling chain.doFilter(). If 'false' create a dummy Keycloak-Account set the roles you need and set it to the context.
4. By the way the roles are also outsourced to the application.yml
5. Skip the rest of the filters in the Security-Chain (so no keycloak-stuff is executed and the corresponding Authorization happend)
In Detail:
1. Class of Custom-Filter
public class CustomFilter extends OncePerRequestFilter {
#Value("${securityEnabled}")
private boolean securityEnabled;
#Value("${grantedRoles}")
private String[] grantedRoles;
#Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
if (!securityEnabled){
// Read roles from application.yml
Set<String> roles = Arrays.stream(grantedRoles)
.collect(Collectors.toCollection(HashSet::new));
// Dummy Keycloak-Account
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(null, null, null, null, null, null, null);
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<>("Dummy_Principal", session);
final KeycloakAccount account = new SimpleKeycloakAccount(principal, roles, principal.getKeycloakSecurityContext());
// Dummy Security Context
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(new KeycloakAuthenticationToken(account, false));
SecurityContextHolder.setContext(context);
// Skip the rest of the filters
req.getRequestDispatcher(req.getServletPath()).forward(req, res);
}
chain.doFilter(req, res);
}
}
2. Insert Custom-Filter in the http-Configuration of Spring-Security
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.cors()
.and()
.csrf()
.disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.addFilterAfter(CustomFilter(), CsrfFilter.class)
.authorizeRequests()
.anyRequest().permitAll();
}
Have a look at the default Filter-Chain after configuring Keycloak:
Filter-Chain
So it´s obvious to insert the Custom-Filter at position 5 to avoid the whole Keycloak-Magic.
I have used this workaround to defeat the method security and it´s #Secured-Annotation.

Updated answer for spring boot 2.5.6 and keycloak 16.1.0
Set this in your application.properties:
spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakAutoConfiguration
(The autoconfig class name has changed since earlier answers)
The keycloak adapter dependency brings in the standard spring security autoconfig too, so if you want to disable both use this:
spring.autoconfigure.exclude=org.keycloak.adapters.springboot.KeycloakAutoConfiguration,org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

Related

Spring Cloud Gateway with spring-boot-starter-web

I am creating gateway for Spring Boot microservices using Spring Cloud Gateway. Gateway is also responsible for JWT authorization using Spring Security.
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
...
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader(JwtProperties.HEADER_STRING);
if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
Authentication authentication = getUsernamePasswordAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request) {
String token = request.getHeader(JwtProperties.HEADER_STRING).replace(JwtProperties.TOKEN_PREFIX, "");
DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes())).build().verify(token);
String username = decodedJWT.getSubject();
if (username != null) {
UserPrincipal principal = (UserPrincipal) userPrincipalService.loadUserByUsername(username);
Authentication auth = new UsernamePasswordAuthenticationToken(username, null, principal.getAuthorities());
return auth;
}
return null;
}
}
This filter is registred in configure method like this:
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userPrincipalService))
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
...
.anyRequest().authenticated();
}
...
}
As you can see, Spring Security is using HttpServletRequest, HttpServletResponse, FilterChain interfaces which belong to spring-boot-starter-web. But that is main problem beacause it's incompatible with spring cloud gateway.
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.
Is there any way to avoid this error or any different solution for implementing jwt authorization filter on gateway?
Thanks!
In the Documentation of spring cloud gateway it is explicitely stated that this product runs on top of Netty and requires webflux, hence it's not compatible with spring MVC.
The filter that you use (JwtAuthorizationFilter) is something that belongs to the non-reactive world so you probably should rewrite it with spring security for web flux building blocks.
Disclaimer, I'm not a spring web flux / spring-security expert, but please consider checking This application - it shows how to define JWT secured application with a reactive version of spring security.
So bottom line you should choose whether you want a reactive application or a traditional one and use the relevant technologies but you can't really mix them.

Spring Security OAuth - how to disable login page?

I want to secure my application with Spring Security, using OAuth 2. However, I don't want the server to redirect incoming unauthorized requests, but instead to respond with HTTP 401. Is it possible?
Example: this code redirects requests to a default login page.
application.properties
spring.security.oauth2.client.registration.google.client-id=...
spring.security.oauth2.client.registration.google.client-secret=...
AuthConfig.java
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
// https://stackoverflow.com/questions/31714585/spring-security-disable-login-page-redirect
// deos not work
// .and()
// .formLogin().successHandler((request, response, authentication) -> {});
}
}
You need to create new authentication entry point and set it in configuration.
#Configuration
public class AuthConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/secured/**").authenticated()
.anyRequest().permitAll()
.and()
.oauth2Login();
}
}
public class AuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AuthenticationEntryPoint() {
super("");
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(401, "Unauthorized");
}
}
You need to set oauth2Login.loginPage in your HttpSecurity config and create a controller mapping to return whatever you want. Here's a simple example.
So in your security config
http
.authorizeRequests()
.antMatchers("/noauth").permitAll()
.oauth2Login()
.loginPage("/noauth")
In a controller
#GetMapping("/noauth")
public ResponseEntity<?> noAuth() {
Map<String, String> body = new HashMap<>();
body.put("message", "unauthorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(body);
}
You can pass a map or pojo to the body method.
I would like to expand on Petr's answer by explaining that apparently for the time being first of all, the default login page is shown when there are more than one OAuth2 configured providers. I would expect that Spring Boot would have a smart trick to bypass this page easily and choose the right provider automatically, basing e.g. on the existence of the provider's client ID in the original request. I found out the hard way that this is not the case. So the way to do this is.. this not very apparent trick of providing a custom handler for failures - that will REDIRECT the user to the correct OAuth2 endpoint for each provider, based on the original HTTP request URL. I tried this and it works and I spent a whole day trying all manners of other solutions - my original scenario was to pass additional parameters to OAuth2 scheme in order to be able to get them back on successful authentication - they used to do this appending Base64 encoded information to the "state" URL request parameter, but Spring Security does not allow this at the moment. So the only alternative was to call a Spring Security-protected URL with those parameters already there, so when the successful authentication happens, this URL is accessed again automatically with those parameters intact.
Related: Multiple Login endpoints Spring Security OAuth2

Understanding Spring Security #EnableOAuth2Client annotation

I'm having a problem implementing OpenID connect built on Spring Security Oauth2 library. (Read more about the problem in a separate question.) While researching it, I read the documentation for the #EnableOauth2Client annotation, which says:
Enable configuration for an OAuth2 client in a web application that uses Spring Security and wants to use the Authorization Code Grant from one or more OAuth2 Authorization servers. To take advantage of this feature you need a global servlet filter in your application of the DelegatingFilterProxy that delegates to a bean named "oauth2ClientContextFilter". Once that filter is in place your client app can use another bean provided by this annotation (an AccessTokenRequest) to create an OAuth2RestTemplate, e.g.
#Configuration
#EnableOAuth2Client
public class RemoteResourceConfiguration {
#Bean
public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) {
return new OAuth2RestTemplate(remote(), oauth2ClientContext);
}
}
Client apps that use client credentials grants do not need the AccessTokenRequest or the scoped RestOperations (the state is global for the app), but they should still use the filter to trigger the OAuth2RestOperations to obtain a token when necessary. Apps that us [sic] password grants need to set the authentication properties in the OAuth2ProtectedResourceDetails before using the RestOperations, and this means the resource details themselves also have to be per session (assuming there are multiple users in the system).
A Note About Versions and Documentation: this documentation is the 2.0.4 release, which is all that is linked to from the Spring Security project page even for the newer 2.3.5 link, which my project is using. Our other Spring versions: Spring Boot 1.3.0, Spring Security 3.2.5, Spring Framework 4.2.3.
I don't understand quite what it means, particularly
a global servlet filter in your application of the DelegatingFilterProxy that delegates to a bean named "oauth2ClientContextFilter"
Here is how we are configuring our rest template.
#Configuration
#EnableOAuth2Client
public class OpenIdConnectConfig {
#Bean
public OAuth2ProtectedResourceDetails openIdResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
details.setScope(oidcScopes);
details.setPreEstablishedRedirectUri(redirectUri);
details.setUseCurrentUri(false);
return details;
}
#Bean(name = "my.company.ui.security.OpenIdRestTemplate")
// ToDo: fix org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread
public OAuth2RestTemplate OpenIdRestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(openIdResourceDetails(), clientContext);
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
#Value("${oidc.clientId}")
private String clientId;
#Value("${oidc.clientSecret}")
private String clientSecret;
#Value("${oidc.accessTokenUrl}")
private String accessTokenUri;
#Value("${oidc.userAuthorizationUri}")
private String userAuthorizationUri;
#Value("${oidc.redirectUri}")
private String redirectUri;
#Value("#{'${oidc.scopes}'.split(',')}")
private List<String> oidcScopes;
}
The filter that performs the authentication (some exception handling and user processing code removed):
public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter {
public OpenIdConnectFilter(
RequestMatcher requiresAuthenticationRequestMatcher,
AuthenticationService authenticationService
) {
super(requiresAuthenticationRequestMatcher);
setAuthenticationManager(new NoopAuthenticationManager());
}
#SuppressWarnings("RedundantThrows") // Matching overridden method
#Override
public Authentication attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException, IOException, ServletException {
// Required parameters (one-time access code, state) are retrieved from the context
OAuth2AccessToken oAuth2AccessToken = restTemplate.getAccessToken();
// Process the token, get the user details, return an Authentication object.
}
public void setRestTemplate(OAuth2RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
private static class NoopAuthenticationManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(OpenIdConnectFilter.class);
#Value("${oidc.clientId}")
private String clientId;
#Value("${oidc.issuer}")
private String issuer;
#Value("${oidc.jwt.jwk.url}")
private String jwkUrl;
private final AuthenticationService authenticationService;
private OAuth2RestTemplate restTemplate;
}
And the Security Config that sets up the Spring Security FilterProxyChain:
#Configuration
#EnableWebSecurity
#EnableOAuth2Client
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
#SuppressWarnings("unchecked")
protected void configure(HttpSecurity http)
throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.csrf()
.disable()
.authorizeRequests()
.expressionHandler(securityExpressionHandler)
.antMatchers("/asset/**").access("permitAll")
.antMatchers("/ws/ssoEnabled").access("permitAll")
.antMatchers("/**").access("hasRole('ROLE_USER') or hasRole('ROLE_TOKEN_ACCESS')")
.and()
.httpBasic()
.authenticationEntryPoint(ajaxAwareLoginUrlAuthenticationEntryPoint)
.and()
// Handles unauthenticated requests, catching UserRedirectRequiredExceptions and redirecting to OAuth provider
.addFilterAfter(new OAuth2ClientContextFilter(), SecurityContextPersistenceFilter.class)
// Handles the oauth callback, exchanging the one-time code for a durable token
.addFilterAfter(openIdConnectFilter, OAuth2ClientContextFilter.class)
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/logincheck")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(ajaxAwareAuthenticationSuccessHandler)
.failureHandler(ajaxAwareAuthenticationFailureHandler)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.rememberMe()
.rememberMeServices(rememberMeServices)
// Even though this key has been added directly to the rememberMeServices instance, the RememberMeConfigurer
// can instantiate a new RememberMeServices with a made-up key if the same key is not provided.
.key("the key value")
;
// We do not configure a bean for the SessionAuthenticationStrategy. We want to use the Spring default strategy,
// which is configured by the above builder chain. In order to share the correct, configured instance with our
// custom OpenIdConnectFilter, we first tell the builder to perform the configuration (normally this would be
// done long after this method returns)...
http.getConfigurer(SessionManagementConfigurer.class).init(http);
// ... then we get the shared object by interface (SessionAuthenticationStrategy) class name...
final SessionAuthenticationStrategy sessionAuthenticationStrategy = http.getSharedObject(SessionAuthenticationStrategy.class);
// ... then set it in our custom filter.
openIdConnectFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
}
You'll notice multiple authentication mechanisms in there. We're in the process of migrating from Form Login to Oauth login and there is a feature flag in place for the initial release. The flag is working quite well, except for the problem described in my linked question at the top that occurs for a while after flipping the flag, then seems to resolve itself.
Is the configuration I've shown above sufficient fulfillment of the documented instructions for the #EnableOauth2Client annotation?
Or is there something else I need to do with a DelegationFilterProxy? If so, how?

Spring Boot Security PreAuthenticated Scenario with Anonymous access

I have a Spring Boot (1.5.6) application that is using the "pre-authenticated" authentication scenario (SiteMinder) from Spring Security.
I have a need to expose the actuator "health" endpoint anonymously meaning the requests to that endpoint will not go through SiteMinder and as a result, the SM_USER header will not be present in the HTTP Request Header.
The problem I'm facing is that no matter how I try to configure the "health" endpoint, the framework is throwing an org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException because the expected header ("SM_USER") is not present when the request does not go through SiteMinder.
This was my original security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().antMatchers("/cars/**", "/dealers/**")
.hasAnyRole("CLIENT", "ADMIN")
.and()
.authorizeRequests().antMatchers("/health")
.permitAll()
.and()
.authorizeRequests().anyRequest().denyAll()
.and()
.addFilter(requestHeaderAuthenticationFilter())
.csrf().disable();
}
#Bean
public Filter requestHeaderAuthenticationFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthProvider());
}
#Bean
public AuthenticationProvider preAuthProvider() {
PreAuthenticatedAuthenticationProvider authManager = new PreAuthenticatedAuthenticationProvider();
authManager.setPreAuthenticatedUserDetailsService(preAuthUserDetailsService());
return authManager;
}
#Bean
public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthUserDetailsService() {
return new UserDetailsByNameServiceWrapper<>(inMemoryUserDetails());
}
#Bean
public UserDetailsService inMemoryUserDetails() {
return new InMemoryUserDetailsManager(getUserSource().getUsers());
}
#Bean
public UserHolder getUserHolder() {
return new UserHolderSpringSecurityImple();
}
#Bean
#ConfigurationProperties
public UserSource getUserSource() {
return new UserSource();
}
I've tried to exclude the /health endpoint a couple different ways to no avail.
Things I've tried:
Configure health endpoint for anonymous access rather than permitAll:
http
.authorizeRequests().antMatchers("/health")
.anonymous()
Configure WebSecurity to ignore the health endpoint:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health");
}
Turn off security for all actuator endpoints (not idea but I was grasping for straws):
management.security.enabled=false
Looking at the logs, the problem seems to be that the RequestHeaderAuthenticationFilter is getting registered as a top level filter rather than a filter in the existing securityFilterChain:
.s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestHeaderAuthenticationFilter' to: [/*]
Based on my understanding, because the RequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter, the framework knows where to insert the filter within the chain which is why I'm not tinkering with the addFilterBefore or addFilterAfter variants. Maybe I should be? Does anybody know the correct place to insert the filter explicitly? (I thought the need for explicitly specifying filter order was removed in prior versions of Spring Security)
I know I can configure the RequestHeaderAuthenticationFilter so that it doesn't throw an exception if the header is not present but I'd like to keep that on if at all possible.
I found this SO post that seems to be similar to my problem but unfortunately there's no answer there either.
spring-boot-security-preauthentication-with-permitted-resources-still-authenti
Any help would be greatly appreciated!
Thanks!
The problem was indeed the fact that the RequestHeaderAuthenticationFilter was being registered both as a top level filter (unwanted) and also within the Spring Security FilterChain (desired).
The reason for the "double registration" is because Spring Boot will register any Filter Beans with the Servlet Container automatically.
In order to prevent the "auto-registration" I just had to define a FilterRegistrationBean like so:
#Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
Docs:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-disable-registration-of-a-servlet-or-filter
An alternate/simpler solution:
Just don't mark the RequestHeaderAuthenticationFilter class as an #Bean which is fine because there's no kind of DI needed for that particular filter. By not marking the filter with #Bean, Boot won't try to auto register it which removes the need to define the FilterRegistrationBean.
The provided answer is not complete most likely because Sean Casey made so many trial and error changes that lost track of which configuration actually fixed the problem. I am posting my findings which I believe have the correct configuration:
If you have incorrectly registered the RequestHeaderAuthenticationFilter as a #Bean, as the original answer says, then remove it. Just creating it as a normal instance and adding it as a filter registers it properly:
#Override
protected void configure(HttpSecurity http) throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
// configure your filter
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilter(filter)
.csrf().disable();
}
The magical configuration which Sean Casey tried but initially failed (due to the double registering of the auth filter) is the WebSecurity configuration:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health");
}
Notice that adding the HttpSecurity antMatchers does nothing in that case, it seems that WebSecurity is the one taking precedence and controlling what goes through.
EXTRA: Per Spring Security documentation, the WebSecurity.ignore method should be used for static resources, not for dynamic ones which should instead be mapped to allow all users. However in this case it seems the mapping gets overridden by the PreAuthentication filter which forces the use of the more aggressive ignore scenario.

Spring Security / rolesAllowed / antMatchers

I am using Spring Data REST and I have a find method in my repository:
public List<Contact> findByLastNameOrderByLastNameAsc(#Param("lastName") String lastName);
I am trying to add security to the method, but no luck. In my DB, I have 1 user with the role 'ROLE_USER'. When the service starts, the login form comes up and I am able to login with the credentials in the DB.
Here is my web security config:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,identification,enabled from users where username = ?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/contacts/findByLastNameOrderByLastNameAsc").hasRole("ADMIN")
.antMatchers("/contacts/**").fullyAuthenticated()
.antMatchers("/contacts/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin();
}
When I try to invoke the service in my repository, I am not seeing any authentication error. Using my browser, the URL comes up fine, even though the user in the DB does not have the 'ADMIN' role.
I tried adding 'RolesAllowed' to the method in my repository, but no luck:
#RolesAllowed(value = { "ADMIN" })
public List<Contact> findByLastNameOrderByLastNameAsc(#Param("lastName") String lastName);
Am I going about adding security to the REST API provided by Spring Data correctly? Ideas on how to get this to work?
thanks
FWIW: I forgot to add the jsr250 support. So, I added this config:
#Configuration
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
}
Now the RolesAllowed annotation is working fine.

Resources