How can I avoid the duplicate code of mapping in Spring project? - java

I am currently mapping pages using Page Controller.
Every page needs to check for Session, which is a duplicate code.
How do I avoid duplicating this code?
#Controller
public class PageController {
...
#RequestMapping("/view/List")
public String list(Map<String, Object> model) {
String session_chk = Utils.SessionCheck();
if(session_chk.equals("none")){
return "/view/manager/Login";
}
return "/view/member/List";
}
#RequestMapping("/view/Detail")
public String detail(Map<String, Object> model) {
String session_chk = Utils.SessionCheck();
if(session_chk.equals("none")){
return "/view/manager/Login";
}
return "/view/member/Detail";
}
...

You could use Spring Security to avoid all duplicate code related with securing your web application and also it provides buit-in protection against attacks such as session fixation, clickjacking or cross site request forgery. It is the de-facto standard for securing Spring-based applications.
Here you can find a nice series of tutorials to learn Spring Security.
Here you can find an small example in where you'll see how I handled a similar situation to yours using Spring Security configuration only.
This is my Spring Security configuration class
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1Pass")).roles("USER")
.and()
.withUser("user2").password(passwordEncoder().encode("user2Pass")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("adminPass")).roles("ADMIN");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
// Only users with admin role will access views starting with /admin
.antMatchers("/admin/**").hasRole("ADMIN")
// Anonymous users (users without sessions) will access this URL
.antMatchers("/anonymous*").anonymous()
// Allowing all users to access login page
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/homepage.html", true)
.failureHandler(authenticationFailureHandler())
.and()
.logout()
.logoutUrl("/perform_logout")
.deleteCookies("JSESSIONID")
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.exceptionHandling().accessDeniedPage("/accessDenied");
}
#Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Add a filter which will redirect, and add check
#Component
public class SessionFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!request.getRequestURI().contains("view/manager/Login") && "none".equals(Utils.SessionCheck())) {
httpResponse.sendRedirect("view/manager/Login.jsp");
//....
} else {
doFilterInternal(request, response, filterChain);
}
}
You can add it to all urls:
FilterRegistration sessionFilter = servletContext.addFilter("SessionFilter", SessionFilter.class);
sessionFilter.addMappingForUrlPatterns(null, false, "/*");

Related

How to update authentication in auditorAware

#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class JpaAuditingConfiguration {
#Bean
#Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public AuditorAware<String> auditorProvider() {
Authentication authentication
= SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return () -> Optional.<String>empty();
}
return () -> Optional.of(authentication.getName());
}
public static void main(String[] args) {
SpringApplication.run(JpaAuditingConfiguration.class, args);
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailServiceImpl userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/logout/success")
.and().authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.GET, "/logout/**").permitAll()
.anyRequest().authenticated().and()
// Filter for the api/login requests
.addFilterBefore(new LoginFilter("/login",
authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
// Filter for other requests to check JWT in header
.addFilterBefore(new AuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
public class AuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
Authentication authentication
= AuthenticationService
.getAuthentication((HttpServletRequest)request);
SecurityContext securityContext
= SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
auditorProvider seems to be called only once when application starts. authentication is called before it is set in doFilter. authentication object is always null and doesn't update when it's set later in doFilter. When I update a row in db, createdBy and lastModifiedBy are always null.
My doFilter seems to be called when an HTTP request is processed.
I followed a tutorial to learn spring security, but after troubleshooting for a few hours and followed through many similar tutorial, still can't figure out how to properly set the authentication in sequence so when I update a row, spring will update lastModifiedBy and createdBy automatically.
Your issue is that you are creating an anonymous AuditorAware but you are evaluating the SecurityContextHolder.getContext().getAuthentication() outside the body so whatever at that creation time will be kept inside the body.
#Bean
#Scope(value= ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public AuditorAware<String> auditorProvider() {
return () -> {
Authentication authentication
= SecurityContextHolder.getContext().getAuthentication();
return Optional.ofNullable(authentication)
.map(Authentication::getName);
};
}
You could say, you have SCOPE_PROTOTYPE but it is useless if it was autowired into a singleton instance that framework uses.

Spring Security - multiple configurations - add LogoutHandler

I have a spring-boot application using spring-security. The security configuration is split into multiple instances of WebSecurityConfigurerAdapter.
I have one where I configure logout in general:
#Override
protected void configure(HttpSecurity http) throws Exception {
// configure logout
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.addLogoutHandler((request, response, authentication) -> {
System.out.println("logged out 1!");
})
.permitAll();
// ... more security configuration, e.g. login, CSRF, rememberme
}
And there is another WebSecurityConfigurerAdapter, where I want to do almost nothing, except adding another LogoutHandler:
#Override
protected void configure(HttpSecurity http) throws Exception {
// configure logout
http
.logout()
.logoutUrl("/logout")
.addLogoutHandler((request, response, authentication) -> {
System.out.println("logged out 2!");
});
}
Both configure() methods are called. However, if I do log out, only the first LogoutHandler is called. Changing the #Order of both configurations does not change the result.
What is missing in my configuration?
When you create several security configurations Spring Boot will create a separate SecurityFilterChain for each of them. See WebSecurity:
#Override
protected Filter performBuild() throws Exception {
// ...
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// ...
}
When application gets logout request FilterChainProxy will return only one SecurityFilterChain:
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
// Only the first chain that matches logout request will be used:
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
If you really need modular security configuration I would suggest to create a separate security configuration for logout and other realms. You can define logout handlers as beans (using #Bean annotation) in different configuration classes and collect these handlers in logout configuration:
WebSecurityLogoutConfiguration.java
#Configuration
#Order(99)
public class WebSecurityLogoutConfiguration extends WebSecurityConfigurerAdapter {
// ALL YOUR LOGOUT HANDLERS WILL BE IN THIS LIST
#Autowired
private List<LogoutHandler> logoutHandlers;
#Override
protected void configure(HttpSecurity http) throws Exception {
// configure only logout
http
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
// USE CompositeLogoutHandler
.addLogoutHandler(new CompositeLogoutHandler(logoutHandlers));
http.csrf().disable(); // for demo purposes
}
}
WebSecurity1Configuration.java
#Configuration
#Order(101)
public class WebSecurity1Configuration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// ... more security configuration, e.g. login, CSRF, rememberme
http.authorizeRequests()
.antMatchers("/secured/**")
.authenticated();
}
// LOGOUT HANDLER 1
#Bean
public LogoutHandler logoutHandler1() {
return (request, response, authentication) -> {
System.out.println("logged out 1!");
};
}
}
WebSecurity2Configuration.java
#Configuration
#Order(102)
public class WebSecurity2Configuration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**")
.permitAll();
}
// LOGOUT HANDLER 2
#Bean
public LogoutHandler logoutHandler2() {
return (request, response, authentication) -> {
System.out.println("logged out 2!");
};
}
}
You should be solving this problem with the CompositeLogoutHandler on your single /logout operation endpoint.
You can still keep two WebSecurityConfigurerAdapter's as desired, but you'll be conglomerating the logout functionality for two LogoutHandlers into a single composite action:
new CompositeLogoutHandler(loggedOutHandler1, loggedOutHandler2);
The keypoint is you should create separated instance of AuthenticationManger.
Here is an sample for multiples WebSecurityAdapter

Spring security logout not working because of CORS

Technologies: Spring Security, Spring Boot on backend and ReactJs and axios on front-end.
What I want to have: When hitting a logout button on my front-end I want to log out the user. In order to do so I make a call to backend using delete. Then I want my backend to log out.
My issue: When I call Spring Security logout endpoint from my front-end I receive the following message:
Failed to load http://localhost:8080/logout: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8888' is therefore not allowed access. The response had HTTP status code 403. I do understand why I have this error - backend is on localhost:8080 and front-end on localhost:8888. But what I don't understand is why my configuration doesn't work for logout, while it works perfectly fine for all other situations (e.g. calling spring security login endpoint or some of my custom endpoints).
How I make the call from the front-end
const endpoint = 'logout';
return axios.delete(
'http://localhost:8080/' + `${endpoint}`,
{withCredentials: true}
)
.then(response => {
let resp = {
httpCode: response.status,
data: response.data
};
return {response: resp};
})
.catch(error => {
let err = {
httpStatusCode: error.response.status,
message: `Error calling endpoint ${endpoint}`
};
return {error: err};
});
Enabling CORS from front-end
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8888");
}
};
}
SecurityConfig.java - here you might notice some parts are commented - they are other solutions I tried but they didn't work.
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = UsersRepository.class)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private RESTAuthenticationSuccessHandler restAuthenticationSuccessHandler;
#Autowired
private RESTAuthenticationFailureHandler restAuthenticationFailureHandler;
#Bean
public CustomLogoutHandler customLogoutHandler(){
return new CustomLogoutHandler();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("**/anna/**").authenticated()
.anyRequest().permitAll()
.and()
.logout()
.addLogoutHandler(customLogoutHandler())
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.formLogin().successHandler(restAuthenticationSuccessHandler);
http.formLogin().failureHandler(restAuthenticationFailureHandler);
// http
// .logout()
// .logoutUrl("/logout")
// .addLogoutHandler(new CustomLogoutHandler())
// .invalidateHttpSession(true);
// http
// .cors()
// .and()
// .csrf().disable()
// .logout()
// .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
CustomLogoutHandler.java I read somewhere about the solution with setting the header here. I guess it's bad practice and would prefer not to do it, but basically I'd be happy to see the log working. Currently it's not logging anything so my guess it that it's not called on logout.
//#Slf4j
public class CustomLogoutHandler implements LogoutHandler{
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
response.setHeader("Access-Control-Allow-Origin", "*");
System.out.println("TodosLogoutHandler logging you out of the back-end app.");
}
}
I checked the source code of CorsRegistration,
public CorsConfiguration applyPermitDefaultValues() {
[...]
if (this.allowedMethods == null) {
this.setAllowedMethods(Arrays.asList(
HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name()));
}
[...]
return this;
}
As you can see, the Delete method is NOT ALLOWED by default, So you need add the delete method.
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8888")
.addAllowedMethod(HttpMethod.DELETE);
}
};
}

Redirect loop when x509 authentication fails

I have spring boot application with x509 authentication. My problem is that when authentication fails i get redirect loop instead of error screen.
When authentication fails i throw UsernameNotFoundException loadUserDetails from method in ArhivX509UserDetailsService.java
My code is as follows:
SecurityConfiguration.java
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("x509UserDetailsService")
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> iX509UserDetailsService;
#Override
protected void configure(HttpSecurity pHttp) throws Exception {
//#formatter:off
pHttp
.authorizeRequests()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/error401").permitAll()
.anyRequest().authenticated()
.and()
.x509()
.subjectPrincipalRegex("(.*)")
.authenticationUserDetailsService(iX509UserDetailsService)
.and()
.addFilterAfter(new X509ErrorCheckerFilter(), X509AuthenticationFilter.class)
.addFilterBefore(new LoggerMDCFilter(), X509ErrorCheckerFilter.class)
.exceptionHandling()
.authenticationEntryPoint(unauthorizedEntryPoint())
.accessDeniedPage(AppUrls.ERROR_401)
.and()
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.sessionManagement()
.maximumSessions(1)
.and()
.and()
.logout()
.invalidateHttpSession(true)
.deleteCookies("SESSION", "JSESSIONID")
.logoutSuccessUrl("http://www.google.com")
.permitAll();
//#formatter:on
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return new AuthenticationEntryPoint() {
#Override
public void commence(HttpServletRequest pRequest, HttpServletResponse pResponse,
AuthenticationException pAuthException) throws IOException, ServletException {
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
};
}
}
ArhivX509UserDetailsService.java
#Service("x509UserDetailsService")
public class ArhivX509UserDetailsService
implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Autowired
private IUserProfileService iUserProfileService;
#Autowired
private ICheckCertificateService iCheckCertService;
#Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken pToken) throws UsernameNotFoundException {
X509Certificate tCertificate = (X509Certificate) pToken.getCredentials();
String tSubjectDN = tCertificate.getSubjectDN().toString().toUpperCase();
ProfilKorisnika tProfilKorisnika = iUserProfileService.getUserProfile(tSubjectDN);
if (tProfilKorisnika == null) {
throw new UsernameNotFoundException("Pogreška kod prijave korisnika.");
}
return tProfilKorisnika;
}
}
X509ErrorCheckerFilter.java
public class X509ErrorCheckerFilter extends GenericFilterBean {
private static final String DEFAULT_REDIRECT_URL = "/error401";
private static final String[] UNAUTHENTICATED_URLS = { "/webjars/**", "/error", "/error401", "/login",
"/logout" };
#Override
public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
throws IOException, ServletException {
HttpServletRequest tHttpRequest = (HttpServletRequest) pRequest;
HttpServletResponse tHttpResponse = (HttpServletResponse) pResponse;
String tContextRoot = tHttpRequest.getSession().getServletContext().getContextPath();
String tUri = tHttpRequest.getRequestURI().replaceFirst(tContextRoot, "");
if (isUriSecured(tUri)) {
Authentication tAuthentication = SecurityContextHolder.getContext().getAuthentication();
AuthenticationException tException = (AuthenticationException) tHttpRequest
.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if (tException != null || tAuthentication == null) {
RequestDispatcher tRd = tHttpRequest.getRequestDispatcher(DEFAULT_REDIRECT_URL);
tRd.forward(tHttpRequest, tHttpResponse);
return;
}
}
pChain.doFilter(pRequest, pResponse);
}
private boolean isUriSecured(String pRequestURI) {
boolean tResult = true;
for (String tUrl : UNAUTHENTICATED_URLS) {
if (pRequestURI.startsWith(tUrl)) {
tResult = false;
break;
}
}
return tResult;
}
}
If you need more details please ask.
Applying permitAll() to /error401 means that when the security filterchain is finished executing, the request will be processed normally, regardless of whether or not there is an Authentication in the current SecurityContext.
X509ErrorCheckerFilter is forwarding all unauthenticated requests to /error401 from within the filterchain, so permitAll() is never applied. Instead, the forwarded request goes through the filterchain again and fails authentication, causing the circular redirect.
To resolve this, you have several options. Here are a couple:
1. Disable Security for error endpoints
you can disable security for endpoints using web.ignoring() in your SecurityConfiguration class. This is the simplest option.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/error*");
}
2. Implement the ErrorController interface
The security filterchain will not be called for request mappings included in a controller that implements ErrorController.
See Spring's own BasicErrorController for reference(source).
This second option is preferable as it removes any requirement for a redirect in your filter. Instead, it lets Spring Security do the heavy lifting for routing requests through the auth process. As long as there's no authenticated Authentication in the session's SecurityContext when the security filterchain has finished processing, spring security will return a 401 and return the error page specified by your ErrorController.

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