i created a custom authentication failure handler like this:
public class TrackerAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
if(exception.getClass().isAssignableFrom(DisabledException.class)){
setDefaultFailureUrl("/accountRecovery");
}
}
}
and i created a bean like this:
#Bean
public AuthenticationFailureHandler trackerAuthFailureHandler(){
SimpleUrlAuthenticationFailureHandler handler=new SimpleUrlAuthenticationFailureHandler();
return handler;
}
and spring config like this:
#Autowired
private TrackerAuthFailureHandler trackerAuthFailureHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.failureHandler(trackerAuthFailureHandler);
}
But bean not found exception occurred . any ideas?
When you use annotation injection way then you must use #Component in top of class TrackerAuthFailureHandler. like this:.
#Component
public class TrackerAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler{
Related
I'm using spring boot 2.7.0 and want to remove the deprecated WebSecurityConfigurerAdapter. I have multiple configs which use one or more (different) filters. I want to update the configs so they don't use the deprecated WebSecurityConfigurerAdapter anymore like it is done in the official spring docs: https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter.
After changing the config (like done in the docs) the filters don't get called anymore.
This is my config now:
#EnableWebSecurity
#Configuration
public class TestConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/path/**")
.regexMatchers(API_BASE_REGEX + "/ping").permitAll()
.regexMatchers(API_BASE_REGEX + "/registrations").hasAnyAuthority(TestFilter.ROLE_USER, TestFilter.ROLE_USER_INACTIVE)
.addFilterAfter(new TestFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.authorizeRequests().anyRequest().hasAuthority(TestFilter.ROLE_USER)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Filter example:
public class TestFilter extends OncePerRequestFilter {
public static final String ROLE_USER = "TESTUSER";
public static final String ROLE_USER_INACTIVE = "TESTUSER_INACTIVE";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
... (Internal Logic)
filterChain.doFilter(request, response);
}
}
How the config turns out after following the docs:
#EnableWebSecurity
#Configuration
public class TestConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.antMatcher("/api/path/**")
.regexMatchers(API_BASE_REGEX + "/ping").permitAll()
.regexMatchers(API_BASE_REGEX + "/registrations").hasAnyAuthority(TestFilter.ROLE_USER, TestFilter.ROLE_USER_INACTIVE)
.addFilterAfter(new TestFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.authorizeRequests().anyRequest().hasAuthority(TestFilter.ROLE_USER)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
According to documentation at: https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
You need to use the filterChain bean like this:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.antMatcher("/api/path/**")
.regexMatchers(API_BASE_REGEX + "/ping").permitAll()
.regexMatchers(API_BASE_REGEX + "/registrations").hasAnyAuthority(TestFilter.ROLE_USER, TestFilter.ROLE_USER_INACTIVE)
.addFilterAfter(new TestFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.authorizeRequests().anyRequest().hasAuthority(TestFilter.ROLE_USER)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.httpBasic(withDefaults());
return http.build();
}
You can add the ".antMatcher", ".regexMatcher", etc. with the 'authz' call.
Afterwards you can configure login and logout etc. as usual.
Or am I misunderstanding something? After all you posted the same documentation--
Ps: Im new to this formatting, so the code sample might have some problems.
I am having a hard time configuring my spring security. The problem is, my authentication filter always skips my success and failure handlers whenever I authenticate via a custom UsernamePasswordAuthenticationFilter. I don't seem to know why this happens.
First off, I pass the authentication parameter as JSON, and filter out the username and password, then I pass those two parameters into a new UsernamePasswordAuthenticationToken(username, password) then I get the authentication manager and authenticate the returned token. At the point of success full authentication I expect that the success handler should take over but no it doesn't get called at all.
This is my security configuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.and()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/signup")
.permitAll()
.antMatchers("/", "/security/login", "/request", "/request.html")
.authenticated()
.and()
.formLogin()
.loginProcessingUrl("/security/login")
.successHandler(authenticationSuccessHandler())
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.logoutUrl("/logout")
.permitAll()
.and()
.addFilterAfter
(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
//.and()
.userDetailsService(userDetailsServiceBean());
}
The relevant beans are
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceBean());
}
#Bean
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return new JdbcUserDetails();
}
#Bean
public RestAuthenticationSuccessHandler authenticationSuccessHandler(){
return new RestAuthenticationSuccessHandler();
}
#Bean
public RestAuthenticationFailureHandler authenticationFailureHandler(){
return new RestAuthenticationFailureHandler();
}
#Bean
JsonAuthenticationFilter authenticationFilter() throws Exception {
logger.debug("Authenication filter processing loggin request ");
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
The filter is
public class JsonAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
UsernamePasswordAuthenticationToken authRequest = this.getUserNamePasswordAuthenticationToken(request);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
and finally my success handler
class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication)
throws ServletException, IOException {
logger.debug("Successful login");
System.out.println("\n\n\n\n\n\n\n\nresponse here\n\n\n\n\n\n\n");
response.getWriter().write("{This is a login success response}");
response.getWriter().flush();
response.getWriter().close();
}
I have been battling for too long
Spring Security will back off on a given bean configuration when you supply that bean.
So, because you supplied your filter (JsonAuthenticationFilter), Spring Security expects that you'll know best how to compose it.
So, then, you'd instead do:
#Bean
JsonAuthenticationFilter authenticationFilter() {
JsonAuthenticationFilter filter = new JsonAuthenticationFilter();
// .. other configs
filter.setAuthenticationSuccessHandler(new RestAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(new RestAuthenticationFailureHandler());
}
It looks like there is a lot going on, so if that doesn't solve your issue, feel free to put together a sample, say on GitHub, and I'd be happy to look it over.
I am trying to create an independent packageable jar with a custom annotation, which on inclusion in a controller mapping function (and taking userToken as input in header), returns a boolean whether the user is authenticated or now.
// Expected way of inclusion
public #ResponseBody boolean isAuthenticated(#Authenticator(#RequestHeader("userToken")) Boolean isUserAuthenticated) {
return isUserAuthenticated;
}
I know that this won't be the right syntax, since using this code gives the error that RequestMapping cannot be converted to String (and annotations only accept primitive values).
I am also open to other approaches, but it should have the flexibility to return authentication boolean only when needed and not through global interception.
Important: Please note #Authenticator comes from an independent package, imported in the current package through Maven. Would HTTPServletRequest pass in ConstraintValidator.
Use the spring security BasicAuthenticationFilter :
public class MyBasicAuthenticationFilter extends BasicAuthenticationFilter {
private AuthenticationManager authenticationManager;
public MyBasicAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
this.authenticationManager=authenticationManager;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// do you checks here
super.doFilterInternal(request, response, chain);
}
}
Then add this to your security config with something like:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
http.addFilterBefore(new MyBasicAuthenticationFilter(authenticationManager());
}
#Bean
public AuthenticationManager authenticationManager() {
return new MyAuthenticationManager();
}
I'm building a REST service with Spring and using Spring Security. The default solution with loginform doesn't pass for me. Here it's my WebSecurityConfig:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
class PostAuthSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setStatus(200);
}
}
class PostAuthFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setStatus(403);
e.printStackTrace(new PrintWriter(httpServletResponse.getOutputStream()));
}
}
#Autowired
private MySQLUserDetailsManager mySQLUserDetailsManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/registration", "/checkUser", "/").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginProcessingUrl("/login")
.successHandler(new PostAuthSuccessHandler())
.failureHandler(new PostAuthFailureHandler())
.permitAll()
.and().logout()
.permitAll()
.and().exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/scripts/**");
web.ignoring().antMatchers("/styles/**");
web.ignoring().antMatchers("/jquery-validate/**");
web.ignoring().antMatchers("/bootstrap-3.3.6/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(mySQLUserDetailsManager);
}
}
But /login request is always 403 forbidden. I'm understanding, that something redirect me to /error.
UPD: All pages are responding 403 Forbidden, even "/","/checkuser".
I forget, that in standard form there is a csrf key in hidden input. So:
http
.csrf().disable()
In Specific
I want to have HTTP Basic authentication ONLY for a specific URL pattern.
In Detail
I'm creating an API interface for my application and that needs to be authenticated by simple HTTP basic authentication. But other web pages should not be using HTTP basic but rather a the normal form login.
Current Configuration - NOT Working
#Override
protected void configure(HttpSecurity http) throws Exception {
http //HTTP Security
.csrf().disable() //Disable CSRF
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/connect/**").permitAll()
.antMatchers("/", "/register").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/api/**").hasRole("API")
.anyRequest().authenticated()
.and() //HTTP basic Authentication only for API
.antMatcher("/api/**").httpBasic()
.and() //Login Form configuration for all others
.formLogin().loginPage("/login").permitAll()
.and() //Logout Form configuration
.logout().permitAll();
}
Waited for 2 days and didn't get any help here. But my research provided me a solution :)
Solution
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasAnyRole("ADMIN", "API")
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //HTTP with Disable CSRF
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/connect/**").permitAll()
.antMatchers("/", "/register").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and() //Login Form configuration for all others
.formLogin()
.loginPage("/login").permitAll()
.and() //Logout Form configuration
.logout().permitAll();
}
}
}
I dunno if it can be helpful but I couldn't implement the above solution. I found a workaround defining a single Security
#Configuration class
extending
WebSecurityConfigurerAdapter
with both httpBasic() and formLogin() configured. Then I created a custom
CustomAuthEntryPoint implements AuthenticationEntryPoint
that has this logic in the commence method:
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException
{
String urlContext = UtilityClass.extractUrlContext(request);
if (!urlContext.equals(API_URL_PREFIX))
{
String redirectUrl = "urlOfFormLogin"
response.sendRedirect(request.getContextPath() + redirectUrl);
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
Dunno which is the "best practice strategy" about this issue