Migration to the new spring security configuration fails - java

I am currently migrating the version from Spring to the newest one, but having a lot of struggle when it comes to the new SecurityFilterChain settings.
I am having a list of endpoints which should be available for all. Mainly login, registration as well as some callbacks from other partner sides. Before the migration I've just provided the array to the filter and it worked.
The current (new) implemetation looks like this:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable).cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(Endpoints.PUBLIC_ENDPOINTS).permitAll()
.anyRequest().authenticated()
)
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
This gives me a 401 Unauthorized for the /authentication/login endpoint. What I am doing wrong? Is there any good tutorial or example for the new way of creating the filter.
For completeness here my old implementation:
httpSecurity.cors().and().csrf().disable() // dont authenticate this particular request
.authorizeRequests()
.antMatchers(Endpoints.PUBLIC_ENDPOINTS)
.permitAll()
.anyRequest() // all other requests need to be authenticated
.authenticated().and().exceptionHandling() // make sure we use stateless session; session won't be used to
// store user's state.
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

I've finally found the solution to my problem.
It had nothing to do with the code above. The problem was that the Annotation for the application had some parameters for scanBasePackages. I've removed them and everything started working nicely. The commented line is the old one, not working.
//#SpringBootApplication(scanBasePackages = {"de.company.app.data.user", "de.company.app.security"})
#SpringBootApplication

Related

Spring-Security : OAuth2 Set roles in user object before it is stored in the security context

The goal is to manipulate the roles of a user(or simply create a new Authentication object since I'v got a hunch they cant be manipulated directly) before its stored in the context. After a lot of digging I found that the authentication is perfomred in the OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication and later stored using a sessionStrategy.
The part where I am stuck at is telling spring to use/override that filter.
So far I have managed to create a CustomOAuth2ClientAuthenticationProcessingFilter extending the original one.
I tried to instantiate the filter and add it to the filter chane like so.
#Configuration
#EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login**", "/callback/**", "/resources/**", "/static/**", "/webjars/**", "/webjar/**", "/error**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
.logout()
.permitAll()
.logoutSuccessUrl("/")
.and().addFilterAt(createCustomOAuth2ClientAuthenticationProcessingFilter(), AbstractPreAuthenticatedProcessingFilter.class)
;
}
private CustomOAuth2ClientAuthenticationProcessingFilter createCustomOAuth2ClientAuthenticationProcessingFilter(){
OAuth2SsoProperties sso = (OAuth2SsoProperties)this.getApplicationContext().getBean(OAuth2SsoProperties.class);
OAuth2RestOperations restTemplate = ((UserInfoRestTemplateFactory)this.getApplicationContext().getBean(UserInfoRestTemplateFactory.class)).getUserInfoRestTemplate();
ResourceServerTokenServices tokenServices = (ResourceServerTokenServices)this.getApplicationContext().getBean(ResourceServerTokenServices.class);
CustomOAuth2ClientAuthenticationProcessingFilter customFilter=new CustomOAuth2ClientAuthenticationProcessingFilter(sso.getLoginPath());
customFilter.setRestTemplate(restTemplate);
customFilter.setTokenServices(tokenServices);
customFilter.setApplicationEventPublisher(this.getApplicationContext());
return customFilter;
}
but that, as expected, just calls both filters- mine and the default one.
Is there a way to "replace" the original filter functionality without creating serious issues and save the ability to later use the #Secured anotation for example, in order to secure application endpoints ?
I'v read about using postProcessor in the configuration but never found an extensive well expleined answer.I'v also read about custom configuration classes but not for OAuth2 . Any suggestions are welcome.

Security config apply authentication for all matchers when adding 'cors disable'

When I configure my security like that:
httpSecurity
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/**/mes-actus/**").authenticated()
.anyRequest().permitAll()
.and()
// make sure we use stateless session; session won't be used to
// store user's state.
.addFilterBefore(headerUserRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
it authenticates only routes that contains mes-actus and permit all others, that good!
But when I add this instruction: .cors().and().csrf().disable() then it demands authentication for all without exception.
In other words .antMatchers("/**/mes-actus/**").authenticated() is ignored.
if i remove .cors().and().csrf().disable() i got 403 forbidden
Can you try the code below: it allows all /**/mes-actus/** calls:
http
.authorizeRequests()
.antMatchers("/**/mes-actus/**").permitAll()
.anyRequest().authenticated();
http
.csrf().disable()
.cors().disable();

Java Spring Security hasAnyAuthority doesn't works

I have a spring restful application, backend - Spring 2.4.3, frontend - Angular, when I trying to restrict access to individual pages, I get 401 code. I've tried all variations of hasRole () and hasAuthority () nothing helps. What am I doing wrong?
SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login", "/registration").permitAll()
.antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")
.antMatchers("/getAllUsers").permitAll()
.anyRequest().authenticated();
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
/*.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)*/
.cors();
}
Role.java
#XmlType
#XmlEnum
public enum Role implements GrantedAuthority {
ADMIN,
USER,
INTERVIEWER;
#Override
public String getAuthority() {
return this.name();
}
}
Result:
something wrong :(
Based on your provided code, the line .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class) is commented out. I can't speak to what happens when you un-comment that line (since it is a custom filter), but without that line, you have no means of authenticating. This results in your entry point (which is not provided in your example) being invoked, and seems to be returning your 401 status code.
You can test this by commenting out the lines:
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
and adding .formLogin().and() instead. Form Login will provide a default authentication entry point, default authentication filter, and (if using spring boot) default user details service with a randomly generated password printed to your console, which you can use to test logging in. See the docs for more info on this.
A note on testing with hello world (out of the box) configuration: It is a very useful technique to use formLogin() for testing authorization rules (e.g. .antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")) in Spring Security. It allows you to eliminate your authentication mechanism from being the problem. Once you are confident your authorization rules are working, you can move on to configuring your own authentication scheme. When possible, seek to utilize an existing scheme provided by Spring Security, and only create a custom filter when you cannot use an out of the box scheme. You can read about JWT authentication in the docs.

Spring Security Configuration filter any requests except a specific endpoint?

I have the following code
http.authorizeRequests()
.antMatchers("/users/login","/token/refresh").permitAll()
.anyRequest().authenticated()
.and()
.addFilterAfter(new JWTAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)
And I thought it mean do not filter requests that match /users/login or /token/refresh, but filter any request that doesn't match that.
But it still filters on /users/login.
The way I solved in an old project is requiring auth on every endpoint
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterAfter(new JWTAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)
and ignoring security on the desired endpoints
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.POST, "/users/login");
}
Try this.
.antMatchers("/users/login").permitAll()
.antMatchers("/token/refresh").permitAll()
csrf is enabled by Spring Security automatically. Try disabling it.
http.csrf().disable().authorizeRequests().antMatchers("/users/login","/token/refresh").permitAll().anyRequest().authenticated().and().addFilterAfter(new JWTAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)

How to change Login URL in Spring Security

I created an API that provides User authentication and it's login operation is handled on default '/login' path by Spring Security.
I want to change this path to 'api/v1/login'.
this is my security config :
http.cors().and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/h2-console/**/**").permitAll()
.antMatchers(HttpMethod.POST,"/user/register").permitAll()
.antMatchers("/user/activate").permitAll()
.antMatchers("/user/reset-password").permitAll()
.antMatchers("/user/reset-password").permitAll()
.antMatchers("/admin/user").hasRole("ADMIN")
.antMatchers("/roles").permitAll()
.antMatchers("/user/**").hasRole("USER")
.and()
.formLogin().loginProcessingUrl("/api/v1/login")
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.addFilterBefore(new ExceptionHandlerFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtUserDetailService));
I have added this line to change it :
.formLogin().loginProcessingUrl("/api/v1/login")
But it is still working under '/login' path.
"/api/v1/login" return 404.
Is there any way to change it ?
Spring Boot Version : 2.0.0.RELEASE
The function .loginProcessingUrl("/api/v1/login"), specifies the URL to validate the credentials, the URL to validate username and password.
It will only override url to /api/v1/login of POST type, not GET
It will not pass the request to Spring MVC and your controller
For additional customization you can have a look through FormLoginConfigurer
UPDATE v1
Can you also check if your urls under /api/** are all secured?
If yes then try removing the security from /api/v1/login and add permitAll() configuration to this url
Check this post - https://stackoverflow.com/a/27471722/2600196. if it helps your scenario
UPDATE v2 - this helped in the case here
you were not sending the username and password correctly and for that to work refer the things below, in your it was showing up BadCredentialsException. I enabled debug on the application and was able to figure that out.
you need to post the parameters to the url - http://localhost:8080/api/v1/login as below (have also attached the image, screenshot of postman):-
headers:
Content-Type=application/x-www-form-urlencoded
parameters in key value pairs(not in json format, please refer the image):
username=player3
password=pass3
Above you can the response coming up from the index.html like below:-
test static resource
Which you also need to customize.
For Sending the JSON request for username and password, the changes that will work easily for you will be:-
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/h2-console/**/**").permitAll()
.antMatchers(HttpMethod.POST,"/user/register").permitAll()
.antMatchers("/user/activate").permitAll()
.antMatchers("/user/reset-password").permitAll()
.antMatchers("/user/reset-password").permitAll()
.antMatchers("/admin/user").hasRole("ADMIN")
.antMatchers("/roles").permitAll()
.antMatchers("/user/**").hasRole("USER")
//.and()
//.formLogin().loginProcessingUrl("/api/v1/login") // not required any more
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.addFilterBefore(new ExceptionHandlerFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilter(jwtAuthorizationFilter())
.addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtUserDetailService));
http.headers().frameOptions().disable(); // its required for h2-console
}
public JwtAuthenticationFilter jwtAuthorizationFilter() throws Exception {
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());
jwtAuthenticationFilter.setFilterProcessesUrl("/api/v1/login");
return jwtAuthenticationFilter;
}
And the code .formLogin().loginProcessingUrl("/api/v1/login") not required anymore
Further you need to add the success and the failure urls to the application, and to make your login url to fetch json based user credentials, you need to follow up and need to do some more stuff, some useful reference for that - https://stackoverflow.com/a/19501060/2600196
You are extending org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter which itself extendsorg.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter. In this last class, there is a setter called 
setFilterProcessesUrl
 which is intended to do just this:
setFilterProcessesUrl
public void setFilterProcessesUrl(String filterProcessesUrl)
Sets the URL that determines if authentication is required
Parameters: filterProcessesUrl
This is the link to that javadoc section
So in your WebSecurityConfigurerAdapter you could do just like this:
#Bean
public JWTAuthenticationFilter getJWTAuthenticationFilter() {
final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/auth/login");
return filter;
}
And then in your configure method in the same class just reference it instead of creating new instance:
.addFilter(getJWTAuthenticationFilter

Categories