Not called onLogoutSuccess on SimpleUrlLogoutSuccessHandle - java

#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource; // get by Spring
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Here, you are making the public directory on the classpath root available without authentication (e..g. for css files)
.antMatchers("/public/**", "/registration.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.successHandler((request, response, authentication) -> new DefaultRedirectStrategy().sendRedirect(request, response, "/welcome"))
.failureUrl("/login-error.html")
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.permitAll();
}
}
Here is my custom handler: CustomLogoutSuccessHandler.java
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
private static Logger logger = LogManager.getLogger(CustomLogoutSuccessHandler.class);
#Override
public void onLogoutSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
logger.info("onLogoutSuccess:");
response.sendRedirect(request.getContextPath() + "/login.html");
}
}
In my template (users.html) I add a link like this:
<p align="center"><a th:href="#{/logout.html}">Logout</a></p>
But when click the logout link the method onLogoutSuccess is not call on my custom handler class.
Why?

Related

Spring Boot - Spring Security - InvalidBearerTokenException Error Handling

Similar to this question but it got no answers: Spring Security: Handle InvalidBearerTokenException in #ExceptionHandler
I have similar code and I'm trying to catch org.springframework.security.oauth2.server.resource.InvalidBearerTokenException when a user has supplied invalid/expired/bad JWT format.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Autowired
#Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
resolver.resolveException(request, response, null, e);
}
}
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
#Override
protected void configure(HttpSecurity http) throws Exception
{
// other config here
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer().jwt();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
}
}
I've also implemented the #ExceptionHandler of AuthenticationException for custom response.
#ExceptionHandler({AuthenticationException.class})
protected ResponseEntity<Object> handleAuthException(AuthenticationException ex, WebRequest req)
{
CustomResponse response = ...
return new ResponseEntity<>(response, ...);
}
InvalidBearerTokenException is a subclass of AuthenticationException.
Any idea why this AuthenticationEntryPoint code is not catching it? I've also tried adding logging inside the commence method but it's not being called when InvalidBearerTokenException is thrown, but other AuthenticationException does.
You have to specify this AuthenticationEntryPoint inside the OAuth2ResourceServerConfigurer, like so:
#Override
protected void configure(HttpSecurity http) throws Exception
{
// other config here
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer().jwt().and()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
}
When you set it, the Configurer will change the AuthenticationEntryPoint that is used inside the BearerTokenAuthenticationFilter, see here.

Spring Security hasAuthority is not working properly

I have 3 roles Super Admin, Admin and User. I redirect to two different pages after login. If Admin or Super Admin logged in, it will redirect to /dashboard. If User logs in, it will redirect to /pos. But after Admin and User logged in, it shows 403 page every time. I don't understand why.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
String loginPage = "/login";
String logoutPage = "/logout";
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.authorizeRequests()
.antMatchers(loginPage).permitAll()
.antMatchers("/dashboard", "/boxes/**", "/manufacturers/**", "/brands/**",
"/stocks/**", "/suppliers/**", "/saleinvoices/**", "/purchaseinvoices/**",
"/purchases/**", "/sales/**", "/returns/**", "/users/**").hasRole("ADMIN")
.antMatchers("/dashboard", "/stocks/**", "/pos").hasRole("USER")
.antMatchers("/**").hasAuthority("SUPER_ADMIN")
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin()
.loginPage(loginPage).permitAll()
.loginPage("/")
.failureUrl("/login?error=true")
.successHandler(authenticationSuccessHandler)
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher(logoutPage))
.logoutSuccessUrl(loginPage)
.and()
.exceptionHandling();
}
}
#Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("USER")) {
httpServletResponse.sendRedirect("/pos");
} else {
httpServletResponse.sendRedirect("/dashboard");
}
}
}
you can use hasAnyRole() and allow ADMIN and SUPER_ADMIN to access the dashboard

Spring security with annotations: separated paths (multiply entry points)

I'm using Spring Boot with annotations and Spring Security.
I need to implement two different kind of authentication:
ProviderApiAuthenticationProvider only for "/providerrpc" and "/api/(system|provider|drm)/"
TestAuthFilter (Custom authentificator, now empty), only for "/test/**"
In current configuration on both URL's app requests httpBasic authentification and TestAuthFilter::doFilter() also called on both URL's.
So, whats wrong?
WebSecurityConfig.java:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final ProviderApiAuthenticationProvider providerApiAuthenticationProvider;
private final TestAuthFilter testAuthFilter;
#Autowired
public WebSecurityConfig(TestAuthFilter testAuthFilter, ProviderApiAuthenticationProvider providerApiAuthenticationProvider) {
this.testAuthFilter = testAuthFilter;
this.providerApiAuthenticationProvider = providerApiAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(providerApiAuthenticationProvider);
}
#SuppressWarnings("SpellCheckingInspection")
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authenticationProvider(providerApiAuthenticationProvider)
.authorizeRequests()
.regexMatchers(
"^/providerrpc/",
"^/api/(system|provider|drm)/"
)
.hasAuthority(Role.ROLE_PROVIDER_API.getAuthority())
.and()
.httpBasic()
.realmName("Provider API")
.and()
.addFilterBefore(testAuthFilter, BasicAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(
"/test/**"
)
.authenticated()
.anyRequest()
.permitAll()
;
}
}
TestAuthFilter.java:
#Component
public class TestAuthFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO: Later implement via SecurityContextHolder.getContext().setAuthentication();
chain.doFilter(request,response);
}
}
I found solution which provides two independent entry points for authentication in the official documentation: Spring Security: 5.7 Multiple HttpSecurity
Here is the solution:
MultiHttpSecurityConfig.java
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class RestApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/rest/**")
.authorizeRequests()
.anyRequest().hasAuthority(Role.ROLE_USER.getAuthority())
.and()
.httpBasic()
.realmName("Rest API")
.and().csrf().disable()
;
}
}
#Configuration
#Order(2)
public static class TestWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/test**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.realmName("Test zone");
}
}
}

Spring AuthenticationFailureHandler and WebSecurityConfigurerAdapter loginPage()

Edit: Solved. See my comment after this post
I am currently implementing a Webapplication with Spring-Security. I have implemented a custom AuthenticationFailureHandler which checks if a user tried to login with wrong credentials too often (and blocks him for serveral minutes). But normal failed logins should redirect the user to the login page with the parameter error (/login?error). This page shows an error message like "The password you typed in was wrong"
The AutenticationFailureHandler looks like this (without the uninteressting linse of code)
public class CustomAuthenticationHandler implements AuthenticationFailureHandler {
// Some variables
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// some logic here..
request.setAttribute("param", "error");
response.sendRedirect("/login?error");
}
My WebApplicationSecurity class looks like this:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationHandler customAuthenticationHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.permitAll()
.failureHandler(customAuthenticationHandler)
.and()
.logout()
.permitAll();
http.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**")
.permitAll()
.anyRequest()
.authenticated();
http
.csrf()
.disable();
}
#Bean
CustomAuthenticationHandler authenticationHandler() {
return new CustomAuthenticationHandler();
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("*******")
.password("*******")
.roles("USER");
}
}
}
The problem now is that the AuthenticationFailureHandler redirects to /login?error but (i don't know why) another redirect is done to /login.
Can you help me to solve my problem?
Well, i solved it by adding "/login**" to http.authorizeRequests().antMatchers("/css/**", "/img/**", "/js/**")

Spring security - Custom ExceptionTranslationFilter

This question is actually related to this issue problem.
Based on the suggestion from #harsh-poddar, I added the filter accordingly.
However, after adding that it seems like I can't login even with valid credential.
Following is the related code:
SecurityConfig
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// #Bean
// public CustomAuthenticationEntryPoint customAuthenticationEntryPoint() {
// return new CustomAuthenticationEntryPoint();
// }
#Bean
public CustomExceptionTranslationFilter customExceptionTranslationFilter() {
return new CustomExceptionTranslationFilter(new CustomAuthenticationEntryPoint());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//Note : Able to login without this filter, but after adding this, valid credential also fails
.addFilterAfter(customExceptionTranslationFilter(), ExceptionTranslationFilter.class)
// .exceptionHandling()
// .authenticationEntryPoint(new customAuthenticationEntryPoint())
// .and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
}
CustomAuthenticationProvider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
public CustomAuthenticationProvider() {
super();
}
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
final String name = authentication.getName();
final String password = authentication.getCredentials().toString();
if (name.equals("admin") && password.equals("password")) {
final List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
final UserDetails principal = new User(name, password, grantedAuths);
final Authentication auth = new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
return auth;
} else {
throw new BadCredentialsException("NOT_AUTHORIZED");
}
}
#Override
public boolean supports(final Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
CustomExceptionTranslationFilter
#Component
public class CustomExceptionTranslationFilter extends ExceptionTranslationFilter {
public CustomExceptionTranslationFilter(AuthenticationEntryPoint authenticationEntryPoint) {
super(authenticationEntryPoint);
}
}
CustomAuthenticationEntryPoint
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized.");
}
}
p/s : sorry for the basic question, I'm really new in spring & spring security.
The intended design for AuthenticationEntryPoint is to start/initiate an authentication. However, your implementation CustomAuthenticationEntryPoint does not do this. Instead, it simply sends back an unauthorized response. Please see javadoc for AuthenticationEntryPoint for more details on implementation specifics.
Based on your configuration you are using HTTP Basic for authentication:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
This specific configuration will automatically configure BasicAuthenticationEntryPoint which is an implementation of AuthenticationEntryPoint. The BasicAuthenticationEntryPoint will challenge the user with a http response header of WWW-Authenticate: Basic realm="User Realm" to authenticate, as per server protocol.
However, the fact that you are configuring your own CustomAuthenticationEntryPoint it will ultimately override the BasicAuthenticationEntryPoint which is not what you want to do.
The other post recommended this configuration which again is not what you want to do.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());
}
If your main goal is to provide a custom response to the user when authentication fails than I would propose a form login configuration with a configured AuthenticationFailureHandler. Here is the configuration:
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().failureHandler(new DefaultAuthenticationFailureHandler())
.and()
.csrf().disable(); // NOTE: I would recommend enabling CSRF
Your implementation of DefaultAuthenticationFailureHandler would be:
public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// Set status only OR do whatever you want to the response
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
}
}
The AuthenticationFailureHandler is specifically designed to handle a failed authentication attempt.

Categories

Resources