简体   繁体   中英

Spring Security, disable formLogin() for REST requests

I'm using Spring Boot, Spring Security, Spring Data REST, Hibernate. I'm creating a REST API server.

I configured security to access my REST API as:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@CrossOrigin(origins = "*")
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private TenantFilter tenantFilter;


    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(User.PASSWORD_ENCODER);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtTokenFilter() throws Exception {
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    public CookieAuthFilter cookieAuthFilter() throws Exception {
        return new CookieAuthFilter();
    }

    @Bean
    public CustomWebAuthenticationDetailsSource customWebAuthenticationDetailsSource() {
        return new CustomWebAuthenticationDetailsSource();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //********************************************************************
                // PERMIT OPTIONS
                //********************************************************************
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

                //********************************************************************
                //WEBSOCKET
                //********************************************************************
                .antMatchers("/socket/**").permitAll()

                .antMatchers("/images/**").permitAll()

                .antMatchers("/resources/templates/**").permitAll()

                .antMatchers("/api/v1/notifications/**").permitAll()

                //********************************************************************
                // SWAGGER
                //********************************************************************
                .antMatchers("/v2/api-docs", "/swagger-accounts/configuration/ui", "/swagger-accounts", "/swagger-accounts/configuration/security",
                        "/swagger-ui.html", "/webjars/**", "/swagger-resources/**").permitAll()


                .antMatchers("/api/v1/").permitAll()

                .antMatchers("/api/v1/auth/**").permitAll()
                .antMatchers("/api/v1/formLogin/**").permitAll()

                .antMatchers("/api/v1/logout").permitAll()

                .antMatchers("/api/v1/agents/verifyEmail").permitAll()

                .antMatchers("/api/v1/agents/**/verificationToken").permitAll()

                .antMatchers("/api/v1/agents/**/resetPassword").permitAll()

                .antMatchers("/api/v1/agents/**/verifyPasswordReset").permitAll()

                .antMatchers("/api/v1/agents/**/changePassword").permitAll()

                .antMatchers("/api/v1/errors/**").permitAll()

                .antMatchers("/api/v1/verifyCaptcha").permitAll()

                .antMatchers("/api/v1/ping").permitAll()

                .antMatchers("/api/v1/tenants/**").permitAll()

                // all other endpoints are authenticated
                .antMatchers("/**").authenticated()

                // global settings
                .and()
                .csrf()
                .disable();

       http.formLogin().loginPage("/login").permitAll();


        // Custom JWT based security filter
        http.addFilterBefore(tenantFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(cookieAuthFilter(), UsernamePasswordAuthenticationFilter.class);

        http.headers().cacheControl().disable();
    }
}

The goal I want to get is:

  • show a form login when the user try to access to the url like http://myhost/swagger-ui.html#/ in order to be able to use swagger easily

  • avoid form login when the server is called with a REST call (through Angular or Postman or whatever). In fact, otherwise if the auth fails, the server replies with the html code of the login page

is there a way to accomplish this with Spring, without turning off formLogin()?

Other configuration looks fine

If you notice swagger related urls are also are marked as permitAll().

Just remove permitAll part of swagger and add this in last as authenticated() something like this

.antMatchers("/v2/api-docs", "/swagger-accounts/configuration/ui", "/swagger-accounts", "/swagger-accounts/configuration/security",
                        "/swagger-ui.html", "/webjars/**", "/swagger-resources/**").authenticated()

For REST calls just see if they are added in permitAll()

Also Note: The order of the rules matters and the more specific rules should go first

eg

.antMatchers("/admin/**").authenticated()
.antMatchers("/admin/login").permitAll()

Here /admin/login will also be authenticated.

Look at Combining basic authentication and form login for the same REST Api , I believe it meets your requirement.

The only thing I think you may need to change is in ApiWebSecurityConfigurationAdapter since you only ask 403, the content should look like below ( hasRole(String...) could be replaced by authenticated() if you don't like it's too strict).

http.antMatcher("/api/**").authorizeRequests().anyRequest().hasRole("ADMIN")

I don't know if or how you solved this, but i was looking for something similar. So for anybody who is also looking for this here are my thoughts:

You an configure 2 HttpSecurity elements . So you have extends the WebSecurityConfigurerAdapter 2 times. Make your endpoints like this:

  • '/view/**/*' for everything that requires form login
  • '/api/**/*' for everything that juse needs a 403

Then in one security config confiugre that /view/ with .formlogin() In the other security don't configure the .formlogin()

An example of 2 securityConfigs in one app can be found here: https://www.baeldung.com/spring-security-two-login-pages However in that article they use it to show 2 different login pages, you could just use this to not show a formlogin in one case...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM