Pass additional login parameters from custom WebFlux loginForm - spring-boot

I have implemented a Custom ReactiveAuthenticationManager to work with my formLogin.
#Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return new ReactiveAuthenticationManagerAdapter(this::authenticate);
}
private Authentication authenticate(Authentication authentication) throws AuthenticationException {
return new UsernamePasswordAuthenticationToken(
userId, password, authorities);
}
This works fine, the authenticate function is called at the right time. However I must now send in an additional parameter from the login form. I have been able to create a custom login form which send the extra parameter to the endpoint but how do I include it in the Authentication object I get in authenticate???
I found this example that looks straight forward in the implementation but its for Spring MVC and I need to do it in WebFlux.
UPDATE:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.csrf()
.disable()
.authorizeExchange()
.pathMatchers("/login/**")
.permitAll()
.pathMatchers("/**")
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/"))
.authenticationFailureHandler(this::onAuthenticationFailure)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler("/bye"));
return http.build();
}
Update
I have some progress now. By not using the default formLogin on ServerHttpSecurity but instead using my own WebFilter that extends AuthenticationWebFilter im able to do what I want. The issue im getting now is that I dont have a default entry point and also its hard to know if im missing something else magically that the ServerHttpSecurity class does for me.

I try to do it more elegant but it would be far more work. So I settle for this insed. You have to build your SecurityWebFiler chain just like you do. But after you create it you have to find this AuthenticationWebFilter it is a web filter responsible for creation of authentication object. And set your own authentication converter.
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.formLogin()
;
final SecurityWebFilterChain build = http.build();
build.getWebFilters().collectList().subscribe(
webFilters -> {
for (WebFilter filter : webFilters){
if(filter instanceof AuthenticationWebFilter){
AuthenticationWebFilter awf = (AuthenticationWebFilter) filter;
awf.setAuthenticationConverter(new CustomHttpBasicAuthenticationConverter());
}
}
}
);
return build;
}
Example Of the HttpBasicAuthenticationConverter. To be honest I just copied it from the spring source. Hope that helps.
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.function.Function;
public class CustomHttpBasicAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {
private String usernameParameter = "username";
private String passwordParameter = "password";
#Override
public Mono<Authentication> apply(ServerWebExchange exchange) {
return exchange.getFormData()
.map( data -> createAuthentication(data));
}
private UsernamePasswordAuthenticationToken createAuthentication(
MultiValueMap<String, String> data) {
String username = data.getFirst(this.usernameParameter);
String password = data.getFirst(this.passwordParameter);
return new UsernamePasswordAuthenticationToken(username, password);
}
public void setUsernameParameter(String usernameParameter) {
Assert.notNull(usernameParameter, "usernameParameter cannot be null");
this.usernameParameter = usernameParameter;
}
public void setPasswordParameter(String passwordParameter) {
Assert.notNull(passwordParameter, "passwordParameter cannot be null");
this.passwordParameter = passwordParameter;
}
}

Related

Request method 'POST' is not supported

I'm trying to upgrade Spring Boot from 2.7.6 to 3.0.1. I have a problem during the login action. The following is my new WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
private final CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService, CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler) {
this.customUserDetailsService = customUserDetailsService;
this.customizeAuthenticationSuccessHandler = customizeAuthenticationSuccessHandler;
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(customUserDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/").permitAll()
.requestMatchers("/login").permitAll()
.authenticated()
.and()
.csrf().disable()
.formLogin()
.successHandler(customizeAuthenticationSuccessHandler)
.loginPage("/login")
.failureUrl("/login?error=true")
.usernameParameter("email")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true)
.logoutSuccessUrl("/login?logout=true")
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.authenticationProvider(authenticationProvider());
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login?expired=true");
return http.build();
}
// This second filter chain will secure the static resources without reading the SecurityContext from the session.
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
}
Follow my CustomUserDetailService:
#Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserByEmail(String email) {
System.out.println(email);
User user = userRepository.findByEmail(email.toLowerCase());
System.out.println(user.getEmail());
return userRepository.findByEmail(email.toLowerCase());
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email.toLowerCase());
if (user != null) {
List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));;
return buildUserForAuthentication(user, authorities);
} else {
throw new UsernameNotFoundException("username not found");
}
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
}
When I run the application I see the login page, but when I enter the credential and press submit I receive the error:
Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' is not supported]
and Tomcat shows:
HTTP Status 405 – Method Not Allowed Type Status Report
Message Method 'POST' is not supported.
I searched for a solution but really I don't understand where is the problem.
To use multiple HttpSecurity instances, you must specify a security matcher, otherwise the first SecurityFilterChain will process all requests, and no requests will reach the second chain.
See this section of the Spring Security reference documentation.
In your case the SecurityFilterChain called resources is matching all requests, because you don't have a security matcher.
Since the resources chain does not configure formLogin then Spring Security does not create the default /login POST endpoint.
You can fix this by changing requests to:
#Bean
#Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
.securityMatchers((matchers) -> matchers
.requestMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**") // the requests that this SecurityFilterChain will process
)
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().permitAll())
.requestCache().disable()
.securityContext().disable()
.sessionManagement().disable();
return http.build();
}
If you want more details on the difference between authorizeHttpRequests and requestMatchers you can check out this question.
This error typically occurs when the method in the controller is not mapped to a post request. Should be something like:
#RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView login(...

Keycloak return 401 instead of 302 when token expire keyclaok

When the token expires Keycloak normally return 302(redirect to logout) ; however , I want to return 401 instead of 302 in the api response .
I am using spring boot framework , here the keyloack configuration
#KeycloakConfiguration
#EnableGlobalMethodSecurity(jsr250Enabled = true)
public class GlobalSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.and()
.cors()
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("test/login").permitAll()
.anyRequest()
.authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
// Starting from Keycloak Spring Boot Adapter 7.0.0,
// due to some issues, the automatic discovery of the Keycloak configuration
// from the application.properties (or application.yml) file will not work.
// To overcome this problem, we need to define a KeycloakSpringBootConfigResolver bean explicitly in a #Configuration class.
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
but I can not reach in which place It sends 302 when token expire to change the response to 401
According to the documentation, you could use the exceptionHandling() method:
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/web/server/ServerHttpSecurity.html#exceptionHandling(org.springframework.security.config.Customizer)
exceptionHandling
public ServerHttpSecurity
exceptionHandling​(Customizer<ServerHttpSecurity.ExceptionHandlingSpec>
exceptionHandlingCustomizer)
Configures exception handling (i.e. handles when authentication is
requested). An example configuration can be found below:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
// ...
.exceptionHandling((exceptionHandling) ->
exceptionHandling
// customize how to request for authentication
.authenticationEntryPoint(entryPoint)
);
return http.build();
}
Parameters:
exceptionHandlingCustomizer - the Customizer to provide more options for the ServerHttpSecurity.ExceptionHandlingSpec
Returns:
the ServerHttpSecurity to customize
and according to this page, https://github.com/spring-projects/spring-boot/issues/10715
You could simply do:
http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
However I think this will always return 401 UNAUTHORIZED on any (Authentication Exceptions) and not specifically Token Expired.
If you look at the documentation further, you can implement a custom ServerAuthenticationEntryPoint
https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/ServerAuthenticationEntryPoint.html
Example:
#Component
#Slf4j
public class GatewayAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
#Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
// resolve response status
if (ex instanceof AccessDeniedException) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
} else exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// compose error response
final ErrorResponse error = new ErrorResponse(ex, exchange.getRequest().getURI().getPath(),
exchange.getRequest().getMethod(), HttpStatus.UNAUTHORIZED);
exchange.getResponse()
.writeWith(getEncoder().encode(Mono.just(error), exchange.getResponse().bufferFactory(),
ResolvableType.forInstance(error), MediaType.APPLICATION_JSON,
Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix())));
return Mono.error(ex);
}
}

Spring Boot with Spring Security - Two Factor Authentication with SMS/ PIN/ TOTP

I'm working on a Spring Boot 2.5.0 web application with Spring Security form login using Thymeleaf. I'm looking for ideas on how to implement two factor authentication (2FA) with spring security form login.
The requirement is that when a user logs in with his username and password via. the login form, if the username and password authentication is successful an SMS code should be sent to the registered mobile number of the user and he should be challenged with another page to enter the SMS code. If user gets the SMS code correctly, he should be forwarded to the secured application page.
On the login form, along with the username and password, the user is also requested to enter the text from a captcha image which is verified using a SimpleAuthenticationFilter which extends UsernamePasswordAuthenticationFilter.
This is the current SecurityConfiguration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsServiceImpl userDetailsService;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(
"/favicon.ico",
"/webjars/**",
"/images/**",
"/css/**",
"/js/**",
"/login/**",
"/captcha/**",
"/public/**",
"/user/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/", true)
.and().logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll()
.and().headers().frameOptions().sameOrigin()
.and().sessionManagement()
.maximumSessions(5)
.sessionRegistry(sessionRegistry())
.expiredUrl("/login?error=5");
}
public SimpleAuthenticationFilter authenticationFilter() throws Exception {
SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(authenticationFailureHandler());
return filter;
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userDetailsService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
/** TO-GET-SESSIONS-STORED-ON-SERVER */
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
And this is the SimpleAuthenticationFilter mentioned above.
public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "captcha";
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
HttpSession session = request.getSession(true);
String captchaFromSession = null;
if (session.getAttribute("captcha") != null) {
captchaFromSession = session.getAttribute("captcha").toString();
} else {
throw new CredentialsExpiredException("INVALID SESSION");
}
String captchaFromRequest = obtainCaptcha(request);
if (captchaFromRequest == null) {
throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
}
if (!captchaFromRequest.equals(captchaFromSession)) {
throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
}
UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) {
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
return new UsernamePasswordAuthenticationToken(username, password);
}
private String obtainCaptcha(HttpServletRequest request) {
return request.getParameter(SPRING_SECURITY_FORM_CAPTCHA_KEY);
}
}
Any ideas on how to approach this ? Thanks in advance.
Spring Security has an mfa sample to get you started. It uses Google Authenticator with an OTP, but you can plug in sending/verifying your SMS short-code instead.
You might also consider keeping the captcha verification separate from the (out of the box) authentication filter. If they are separate filters in the same filter chain, it will have the same effect with less code.

Auth websocket session after manual web auth

I am using Spring Security with STOMP WebSocket on SpringBoot. Auth on websocket worked fine with this config when I used simple login form:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/webjars/**", "/resources/**").permitAll()
.antMatchers("/register").anonymous()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(customLoginSuccessHandler)
.failureUrl("/login?error")
.permitAll()
.and()
.csrf().disable()
.logout().logoutSuccessHandler(customLogoutSuccessHandler);
}
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpTypeMatchers(CONNECT).authenticated()
.simpSubscribeDestMatchers(Channel.SYSTEM_ERROR.value()).permitAll()
.simpDestMatchers("/app/publish*").hasRole("USER")
.simpSubscribeDestMatchers("/user/**", "/topic/**", "/system/*").hasRole("USER")
.anyMessage().denyAll();
}
But when I wanted to manually auth client after register new user in RegisterController:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String signup(#Valid #ModelAttribute SignupForm signupForm, Errors errors) {
if (errors.hasErrors()) {
return SIGNUP_VIEW_NAME;
}
User user = signupForm.createAccount();
try {
userService.persist(user);
} catch (EntityExistsException ex) {
errors.rejectValue("login", "user.exists");
return SIGNUP_VIEW_NAME;
}
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, null, Collections.singletonList(new SimpleGrantedAuthority("USER"))));
return "redirect:/";
}
I've got problem with auth websocket. When I get redirected to page where websocket connects I am getting org.springframework.security.access.AccessDeniedException: Access is denied
So. Problem was in define Role. In controller when I defined new SimpleGrantedAuthority("USER") it should be "ROLE_USER" because Spring adds refix ROLLE_ by default. Sure we can change default behaviour of this by add next in WebSecurity configuration
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/favicon.ico");
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
#Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); //remove the prefix ROLE_
return root;
}
});
}
. Yes, dummy mistake but so common. So I will leave it here

How to configure custom authentication filter in spring security - using java config

I'm trying to configure a custom filter for spring security based authentication. It's a simple override of the usernamepasswordfilter. My problem is I don't know how to configure it using java configuration. Every time I hit "/admin/login" - it's entering my filter and causing an exception rather than going to the login page - but the antmatchers should be allowing access to /admin/login.
If I disable my filter, it works fine. I've read a few of the related questions but none seem to lead me to an answer.
Can anyone advise how to fix my configuration below to support the custom filter?
/**
* the security configuration.
*/
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
UserNotifier userNotifier() {
UserNotifier us = new UserNotifier();
return us;
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
SystemUserAuthenticationProvider impl = new SystemUserAuthenticationProvider();
/* other properties etc */
return impl ;
}
#Bean
SystemUserService systemUserService(){
SystemUserService systemUserService = new SystemUserService();
return systemUserService;
}
#Bean
SystemAuthenticationFilter systemAuthenticationFilter() throws Exception {
SystemAuthenticationFilter f = new SystemAuthenticationFilter();
f.setAuthenticationManager(this.authenticationManager());
f.setPasswordParameter("password");
f.setUsernameParameter("email");
f.setPostOnly(true);
f.setAuthenticationFailureHandler(exceptionMappingAuthenticationFailureHandler());
f.setAuthenticationSuccessHandler(savedRequestAwareAuthenticationSuccessHandler());
f.setFilterProcessesUrl("/login");
return f;
}
#Bean
SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler(){
SavedRequestAwareAuthenticationSuccessHandler sv = new SavedRequestAwareAuthenticationSuccessHandler();
sv.setDefaultTargetUrl("/admin/customers");
return sv;
}
#Bean
AuditorAware<SystemUser> auditorAware(){
SystemUserAuditorAware adw = new SystemUserAuditorAware();
return adw;
}
#Bean
ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler(){
ExceptionMappingAuthenticationFailureHandler ex = new ExceptionMappingAuthenticationFailureHandler();
Map<String, String> mappings = new HashMap<String, String>();
mappings.put("org.springframework.security.authentication.CredentialsExpiredException", "/admin/login?reset");
mappings.put("org.springframework.security.authentication.LockedException", "/admin/login?locked");
mappings.put("org.springframework.security.authentication.BadCredentialsException", "/admin/login?error");
mappings.put("org.springframework.security.core.userdetails.UsernameNotFoundException", "/admin/login?error");
ex.setExceptionMappings(mappings);
return ex;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**")
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/login", "/admin/login/new**", "/admin/register", "/admin/logout", "/assets/**", "/admin/session/timeout").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().permitAll()
.and()
.formLogin()
.failureHandler(exceptionMappingAuthenticationFailureHandler())
.loginProcessingUrl("/login")
.loginPage("/admin/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/admin/orders")
.and()
.logout()
.logoutUrl("/logout")
.and()
.requiresChannel()
.antMatchers("/admin/**").requiresSecure()
.and()
.addFilterBefore(systemAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Never mind, I fixed it by the changing the regex on the login processing url. It seemed to be interfering with the previous antmatcher.
So by changing the login processing url in the form login and custom filter configurations to "log_in", the login page now remains accessible without authorisation.

Resources