restrict put, delete and options request in springboot - java

I have used following code to restrict put, delete and options request in my web app but the test response shows 200 OK.
Any suggestion?
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.and().formLogin().loginPage("/login").permitAll()
.and().logout().logoutSuccessHandler(customLogOutHandler()).permitAll();
http.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").denyAll()
.antMatchers(HttpMethod.PUT, "/**").denyAll()
.antMatchers(HttpMethod.DELETE, "/**").denyAll();
super.configure(http);
http.cors();
}

Remove super.configure(http); which will override your configuration with below and denyAll() will not deny the request with methods
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}

Try to configure Spring Security so that access to particular URL patterns are secured differently depending on the HTTP method used to access the URL pattern.
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS,"/path/to/deny").denyAll();
http.authorizeRequests().antMatchers(HttpMethod.DELETE,"/you/can/alsoSpecifyAPath").denyAll();
http.authorizeRequests().antMatchers(HttpMethod.PUT,"/and/can/haveWildcards/*").denyAll();

Related

Spring security filter chain is not working

I am using Spring Security to validate my GET and POST requests. The auth mechanisms for GET and POST are not the same. The below snippet is from my SecurityConfigs configure method.
FilterA is for GET request and I have defined a customBAuthenticationManager bean which implements AuthenticationManager for it.
FilterB is for POST requests and I have defined customAuthProvider with UserDetails service. These GET and POST requests work fine when added alone. But when both these filters are added one after the other, first request in the filter chain fails but the second request works fine.
For instance, with the below code, my POST request works fine but GET request (1st in the chain) throws 401 error. If I swap the order of GET and POST, then the GET would work fine but POST (1st in the chain) throws 403 error.
But all the cases, I could see that the custom authentication manager/provider work fine.
Can someone help me understand what's going wrong here?
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
FilterA filtera = new FilterA();
filtera.setCheckForPrincipalChanges(true);
filtera.setAuthenticationManager(customBAuthenticationManager());
FilterB filterb = new FilterB();
filterb.setCheckForPrincipalChanges(true);
filterb.setAuthenticationManager(authenticationManager());
httpSecurity
.headers()
.frameOptions()
.disable()
.and()
.mvcMatcher("/**")
.csrf()
.disable()
.requestCache()
.requestCache(getHttpSessionRequestCache())
.and()
.sessionManagement()
.maximumSessions(1)
.and()
.and()
.addFilter(filtera)
.authorizeRequests()
.mvcMatchers("/getrequest/**").authenticated()
.and()
.addFilter(filterb)
.authenticationProvider(customAauthProvider())
.authorizeRequests()
.mvcMatchers("/postrequest/**").authenticated()
.and()
.authorizeRequests()
.mvcMatchers("/different-open-request/**").permitAll()
.and()
.httpBasic();
Tried changing the order of the filters in filter chain. Tried removing one of the request from the filter chain, and that works fine.
i suggest you to you use AuthenticationEntryPoint
and then add filters before or after entryPoint.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers().frameOptions().disable()
.and().requestCache().requestCache(getHttpSessionRequestCache())
.and().exceptionHandling().authenticationEntryPoint(authenticationEntrypoint)
.and().authorizeRequests().anyRequest().authenticated().and()
.addFilterAfter(new myAFilter(), BasicAuthenticationFilter.class).addFilter(new Filter() {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest,servletResponse);
}
});
}

Custom failureHandler throws 401 in Spring security login

I'm working on a custom login failureHandler in my app, and I noticed that if I type anyRequest().authenticated() in authorizeRequest() everything works, but I have no CSS on my login page, but if I type anyRequest().permitAll(), I have CSS on my site, and logging with wrong credentials throws 401 - unauthorized error. It only happens in my custom failureHandler. Can you tell me why it happens?
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.antMatchers("/user/**")
.hasAnyRole("USER", "ADMIN")
.antMatchers("/guest*")
.permitAll()
.anyRequest()
.permitAll()
// .anyRequest()
// .authenticated()
.and()
.formLogin()
.loginPage("/guest/login")
.permitAll()
.defaultSuccessUrl("/user/all-tasks", true)
.failureUrl("/guest/login")
.failureHandler(new MyAuthenticationFailureHandler())
.and()
.logout()
.logoutUrl("/user/logout")
.deleteCookies("JSESSIONID")
.and()
.cors()
.and()
.csrf()
.disable();
}
#Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
request.getSession().setAttribute("error", true);
}
}
logging with wrong credentials throws 401 - unauthorized error
Because you are doing :
.failureUrl("/guest/login")
.failureHandler(new MyAuthenticationFailureHandler())
and what are done by failureUrl() will be reset by the subsequent failureHandler().So the customised SimpleUrlAuthenticationFailureHandler do not configure with failureUrl yet and hence it will send 401 if the authentication fails since it does know which URL to redirect to.Change it to :
.failureHandler(new MyAuthenticationFailureHandler("/guest/login"))
should redirect to "/guest/login" if authentication fails.
I noticed that if I type anyRequest().authenticated() in
authorizeRequest() everything works, but I have no CSS on my login
page, but if I type anyRequest().permitAll(), I have CSS on my site,
Because in case of anyRequest().authenticated() , the CSS 's URL also required an authenticated user to access. But in login page , the user must not be authenticated. Because if they are authenticated , it does not make sense that they can go to login page.So no CSS will be shown in login page since only unauthenticated users can go to it.
You have to exclude all the related url resources required by login page to work from any protections by configuring WebSecurity. Everyone should access them :
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/css/**")
.antMatchers("/anyThingRequiredByLoginPageToWork/**");
}

Implement logout in Spring for Single Page Application

I'm using Java Web Token (JWT) for authentication in my web app. I want to create a /logout Rest endpoint that deletes the JSession cookie on the client side, invalidates the session AND does whatever else is necessary.
The front end is a SPA written in React.
I have the following configure method in the WebSecurityConfig class that extends WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.and()
.authorizeRequests()
.antMatchers("/signup").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(
new JWTAuthenticationFilter(userDetailsServiceBean()),
UsernamePasswordAuthenticationFilter.class);
}
I had the following code, but it returns 404 Not found error with the path set to /login. I want to get a HTTP response of 200 and some cleanups instead. what should I do ?
.logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler).logoutSuccessUrl("/login").invalidateHttpSession(true)
After some research I found out that if I want to use stateless Rest API design, I shouldn't use any cookie, including JSESSIONID.
Therefore I changed my code to:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(
new JWTAuthenticationFilter(userDetailsServiceBean()),
UsernamePasswordAuthenticationFilter.class);
}
Could you first try for login request like this. First add JWTLoginFilter in your WebSecurityConfig.
Pasting code from my sample project:
http.csrf().disable() // disable csrf for our requests.
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers(HttpMethod.POST,"/login").permitAll()
.anyRequest().authenticated()
.and()
// We filter the api/login requests
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
// And filter other requests to check the presence of JWT in header
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
You wouldn't require CORSFilter if your front end and back end are on same server.
Also find below JWTLoginFilter class
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter
{
private TokenAuthenticationService tokenAuthenticationService;
public JWTLoginFilter(String url, AuthenticationManager authenticationManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authenticationManager);
tokenAuthenticationService = new TokenAuthenticationService();
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
AccountCredentials credentials = new ObjectMapper().readValue(httpServletRequest.getInputStream(), AccountCredentials.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
return getAuthenticationManager().authenticate(token);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication)
throws IOException, ServletException {
String name = authentication.getName();
tokenAuthenticationService.addAuthentication(response, name);
}
}
AccountCredential class is simple POJO class containing two fields username and password, which I was using to receive the request.
Also please note that UsernamePasswordAuthenticationFilter that we are using require two fields in login request 'username' and 'password'. Like {"username":"user1","password":"secret1"}

How to fix role in Spring Security?

I'm trying to use Spring Security in my project, here is the code:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
//super.configure(auth);
//auth.inMemoryAuthentication().withUser("admin").password("1111").roles("USER");
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username, password, 1 from users where username=?")
.authoritiesByUsernameQuery("select users_username, roles_id from roles_users where users_username=?")
.rolePrefix("ROLE_");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
http
.httpBasic();
http
.authorizeRequests()
.anyRequest().authenticated();
http
.authorizeRequests()
.antMatchers("/users/all").hasRole("admin")
.and()
.formLogin();
http
.exceptionHandling().accessDeniedPage("/403");
}
Here is the problem:
Imagine we have two users (one with the user role and the other one with the admin role) in our database one admin and the second is a user, the problem is when I connect as user (which has only user role) it can access to admin resources (and this is not the expected behavior).
I think the problem in this query:
"select username, password, 1 from users where username=?"
According that username is the primary key?
If anyone has an idea how I can resolve this problem?
Your first matcher anyRequest() is always applied, because the order of matchers is important, see HttpSecurity#authorizeRequests:
Note that the matchers are considered in order. Therefore, the following is invalid because the first matcher matches every request and will never get to the second mapping:
http.authorizeRequests().antMatchers("/**").hasRole("USER").antMatchers("/admin/**")
.hasRole("ADMIN")
Your modified and simplified configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/users/all").hasRole("admin")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
The problem is with the ordering of your rules when you configure your HttpSecurity. What's happening is when the request comes in and hits the
authorizeRequests().anyRequest().authenticated()
and since the user is authenticated it never makes it to the
.antMatchers("/users/all").hasRole("admin")
Here is an example of how you can configure it:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/public").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling().accessDeniedPage("/403");
}
It uses the chain of responsibility pattern. It will go through the chain of rules until it finds a rule that matches. Any rules that come after the rule that matches are never reached. Generally when writing the rules for authenticated requests, the more specific rule will come first.

Spring Security session/xsrf configuration by path

I have an existing Web application that uses spring security for authentication. It is also using session management to allow the user to be logged in for a predefined period of time, and XSRF tokens to prevent XSS attacks.
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.exceptionHandling()
.authenticationEntryPoint(restEntryPoint())
.and()
.headers().addHeaderWriter(new StaticHeadersWriter("Server",""))
.and()
.httpBasic()
.authenticationEntryPoint(restEntryPoint())
.and()
.logout().addLogoutHandler(myLogoutHandler())
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.authorizeRequests()
.antMatchers("/index.html", "/login", "/").permitAll()
.antMatchers(HttpMethod.OPTIONS).denyAll()
.antMatchers(HttpMethod.HEAD).denyAll()
.anyRequest().authenticated()
.and()
.authenticationProvider(myAuthenticationProvider)
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(csrfHeaderFilter(), SessionManagementFilter.class);
// #formatter:on
}
This works great for the Web application. However, now I am requested to add a configuration that would let third party client applications to invoke my services via pure REST calls, i.e. they should be completely stateless and use http basic authentication - no session should be created and xsrf should be disabled (I think...).
I can define a shared URL path for all those client API calls. But how can I leverage my existing security configuration and server to support both requirements?
Answering my own question...
Spring security allows you use multiple configurations based on order. In the documentation, it gives the following example:
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) { 1
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
#Configuration
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration 4
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
In the example above, /api would be permitted only for ADMIN roles, while other paths will be configured with the default FormLoginWebSecurityConfigurerAdapter.
See more at this URL:

Categories

Resources