I'm trying to restrict access to an endpoint based on roles. I searched here on stackoverflow for a solution and tried a few but was not successful in solving my problem. I tried to make the restriction both in the global configuration (WebSecurityConfig) and also with PreAuthorize Annotation, both returned 403 when called. My test user has the SYS_ADMIN role, I have already validated this flow and it actually has the role. Below are my sources.
Global WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.headers().frameOptions().disable();
httpSecurity.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/spring-security-rest/**").permitAll().and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/authentication/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/persons/**").hasRole("SYS_ADMIN")
.antMatchers(HttpMethod.GET, "/api/roles/**").permitAll()
.anyRequest().authenticated().and()
.headers().frameOptions().sameOrigin().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**");
web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs",
"/webjars/**");
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Person Controller
package br.com.aquamain.backend.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import br.com.aquamain.backend.exception.InvalidPersonException;
import br.com.aquamain.backend.model.CustomPage;
import br.com.aquamain.backend.model.Person;
import br.com.aquamain.backend.service.PersonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
#RestController
#RequestMapping("/api/persons")
#Api(tags = "Persons")
public class PersonController {
#Autowired
private PersonService personService;
...
#PostMapping(path = "new", consumes = "application/json")
#ApiOperation(value = "Create a new person.")
public ResponseEntity<?> save(#RequestBody Person person) {
try {
CustomPage<Person> persons = this.personService.save(person);
return new ResponseEntity<CustomPage<Person>>(persons, HttpStatus.CREATED);
} catch (InvalidPersonException error) {
return new ResponseEntity<String>(error.getMessage(), HttpStatus.BAD_REQUEST);
}
}
...
}
Role Model
package br.com.aquamain.backend.model;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
#Entity
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#Table(name = "AQUARIUM_MAINTANCE_MONITOR_ROLE")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_ROLE", nullable = false, precision = 9, scale = 0)
private Integer id;
#JsonIgnoreProperties(value = {"roles"})
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
#JsonInclude(Include.NON_NULL)
private List<User> users;
#Column(name = "CODE_ROLE", nullable = true, length = 255)
private String code;
#Column(name = "DESCRIPTION_ROLE", nullable = true, length = 255)
private String description;
#Column(name = "NAME_ROLE", nullable = true, length = 255)
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Update:
JwtRequestFilter
package br.com.aquamain.backend.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import br.com.aquamain.backend.security.service.JwtUserDetailsService;
import br.com.aquamain.backend.security.utils.JwtTokenUtils;
import io.jsonwebtoken.ExpiredJwtException;
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtils jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Log:
[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/**', OPTIONS], Filters=[]] (1/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/swagger-ui.html/**'], Filters=[]] (2/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/configuration/**'], Filters=[]] (3/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/swagger-resources/**'], Filters=[]] (4/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/v2/api-docs'], Filters=[]] (5/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/webjars/**'], Filters=[]] (6/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6d29e44e, org.springframework.security.web.context.SecurityContextPersistenceFilter#431fc40, org.springframework.security.web.header.HeaderWriterFilter#72dd71b5, org.springframework.web.filter.CorsFilter#258798b6, org.springframework.security.web.authentication.logout.LogoutFilter#48124669, br.com.aquamain.backend.security.JwtRequestFilter#4efcbbde, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#293ce7b0, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#7879d247, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#5e7f5483, org.springframework.security.web.session.SessionManagementFilter#4ce3c16a, org.springframework.security.web.access.ExceptionTranslationFilter#58ad93f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#6f778913]] (7/7)
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.security.web.FilterChainProxy - Securing POST /api/persons/new
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking WebAsyncManagerIntegrationFilter (1/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SecurityContextPersistenceFilter (2/12)
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking HeaderWriterFilter (3/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking CorsFilter (4/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking LogoutFilter (5/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.logout.LogoutFilter - Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking JwtRequestFilter (6/12)
[0;39mHibernate:
/* select
generatedAlias0
from
User as generatedAlias0
where
generatedAlias0.username=:param0 */ select
user0_.ID_USER as id_user1_3_,
user0_.LAST_LOGIN_DATE as last_log2_3_,
user0_.PASSWORD_USER as password3_3_,
user0_.RESET_PASSWORD_TOKEN as reset_pa4_3_,
user0_.USERNAME_USER as username5_3_
from
AQUARIUM_MAINTANCE_MONITOR_USER user0_
where
user0_.USERNAME_USER=?
Hibernate:
/* load br.com.aquamain.backend.model.Person */ select
person0_.ID_PERSON as id_perso1_0_1_,
person0_.BIRTHDAY_PERSON as birthday2_0_1_,
person0_.NAME_PERSON as name_per3_0_1_,
person0_.ID_USER as id_user4_0_1_,
user1_.ID_USER as id_user1_3_0_,
user1_.LAST_LOGIN_DATE as last_log2_3_0_,
user1_.PASSWORD_USER as password3_3_0_,
user1_.RESET_PASSWORD_TOKEN as reset_pa4_3_0_,
user1_.USERNAME_USER as username5_3_0_
from
AQUARIUM_MAINTANCE_MONITOR_PERSON person0_
left outer join
AQUARIUM_MAINTANCE_MONITOR_USER user1_
on person0_.ID_USER=user1_.ID_USER
where
person0_.ID_USER=?
[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking RequestCacheAwareFilter (7/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SecurityContextHolderAwareRequestFilter (8/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking AnonymousAuthenticationFilter (9/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.AnonymousAuthenticationFilter - Did not set SecurityContextHolder since already authenticated UsernamePasswordAuthenticationToken [Principal=Id:1
Username:tony.stark
Last Login:2022-04-12T11:14:30.058-03:00
, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SessionManagementFilter (10/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.s.CompositeSessionAuthenticationStrategy - Preparing session with ChangeSessionIdAuthenticationStrategy (1/1)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking ExceptionTranslationFilter (11/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking FilterSecurityInterceptor (12/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Did not match request to Ant [pattern='/spring-security-rest/**'] - [permitAll] (1/5)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.e.ExpressionBasedFilterInvocationSecurityMetadataSource - Did not match request to Ant [pattern='/api/authentication/**', POST] - [permitAll] (2/5)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.i.FilterSecurityInterceptor - Did not re-authenticate UsernamePasswordAuthenticationToken [Principal=Id:1
Username:tony.stark
Last Login:2022-04-12T11:14:30.058-03:00
, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]] before authorizing
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.i.FilterSecurityInterceptor - Authorizing filter invocation [POST /api/persons/new] with attributes [hasRole('ROLE_SYS_ADMIN')]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.e.WebExpressionVoter - Voted to deny authorization
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.i.FilterSecurityInterceptor - Failed to authorize filter invocation [POST /api/persons/new] with attributes [hasRole('ROLE_SYS_ADMIN')] using AffirmativeBased [DecisionVoters=[org.springframework.security.web.access.expression.WebExpressionVoter#4f47d6c4], AllowIfAllAbstainDecisions=false]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.ExceptionTranslationFilter - Sending UsernamePasswordAuthenticationToken [Principal=Id:1
Username:tony.stark
Last Login:2022-04-12T11:14:30.058-03:00
, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]] to access denied handler since access is denied
[0;39morg.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:113)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at br.com.aquamain.backend.security.JwtRequestFilter.doFilterInternal(JwtRequestFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
[32m2022-04-12 11:14:54 DEBUG o.s.s.w.a.AccessDeniedHandlerImpl - Responding with 403 status code
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match request to [Is Secure]
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/**', OPTIONS], Filters=[]] (1/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/swagger-ui.html/**'], Filters=[]] (2/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/configuration/**'], Filters=[]] (3/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/swagger-resources/**'], Filters=[]] (4/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/v2/api-docs'], Filters=[]] (5/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/webjars/**'], Filters=[]] (6/7)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6d29e44e, org.springframework.security.web.context.SecurityContextPersistenceFilter#431fc40, org.springframework.security.web.header.HeaderWriterFilter#72dd71b5, org.springframework.web.filter.CorsFilter#258798b6, org.springframework.security.web.authentication.logout.LogoutFilter#48124669, br.com.aquamain.backend.security.JwtRequestFilter#4efcbbde, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#293ce7b0, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#7879d247, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#5e7f5483, org.springframework.security.web.session.SessionManagementFilter#4ce3c16a, org.springframework.security.web.access.ExceptionTranslationFilter#58ad93f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#6f778913]] (7/7)
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.security.web.FilterChainProxy - Securing POST /error
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking WebAsyncManagerIntegrationFilter (1/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SecurityContextPersistenceFilter (2/12)
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking HeaderWriterFilter (3/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking CorsFilter (4/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking LogoutFilter (5/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.logout.LogoutFilter - Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking JwtRequestFilter (6/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking RequestCacheAwareFilter (7/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SecurityContextHolderAwareRequestFilter (8/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking AnonymousAuthenticationFilter (9/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking SessionManagementFilter (10/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking ExceptionTranslationFilter (11/12)
[0;39m[32m2022-04-12 11:14:54 TRACE o.s.security.web.FilterChainProxy - Invoking FilterSecurityInterceptor (12/12)
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.security.web.FilterChainProxy - Secured POST /error
[0;39m[32m2022-04-12 11:14:54 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request
Related
After successful basic authentication (login/password in body) when I access to an other secured controller's method I have a 401 error.
I'm not sure but it seems the security context is not set.
Do you have an idea or a line of research?
Use case with swagger
call http://localhost:8080/api/v1/auth/login as admin => success and SecurityContextHolder.getContext() has an authentication as UsernamePasswordAuthenticationToken
call http://localhost:8080/api/v1/auth/me => success. authentication is always UsernamePasswordAuthenticationToken and the user admin is returned
call http://localhost:8080/api/v1/distributors/isauthenticated => failed and return a 401 http status.
The step 3 logs
2023-01-10 10:16:48,523 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to fr.gregoire.passpro.backend.controller.DistributorsController#test()
2023-01-10 10:16:48,524 [http-nio-8080-exec-1] INFO Spring Security Debugger :
************************************************************
Request received for GET '/api/v1/distributors/isauthenticated':
org.apache.catalina.connector.RequestFacade#5d0e637c
servletPath:/api/v1/distributors/isauthenticated
pathInfo:null
headers:
host: localhost:8080
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0
accept: */*
accept-language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
accept-encoding: gzip, deflate, br
referer: http://localhost:8080/swagger-ui/index.html
connection: keep-alive
cookie: JSESSIONID=F6F38EADC8BA2ADF82A519C33507D25F
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
Security filter chain: no match
************************************************************
2023-01-10 10:16:48,524 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to fr.gregoire.passpro.backend.controller.DistributorsController#test()
2023-01-10 10:16:48,524 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet : GET "/api/v1/distributors/isauthenticated", parameters={}
2023-01-10 10:16:48,524 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to fr.gregoire.passpro.backend.controller.DistributorsController#test()
2023-01-10 10:16:48,524 [http-nio-8080-exec-1] DEBUG o.s.s.a.m.AuthorizationManagerBeforeMethodInterceptor : Authorizing method invocation ReflectiveMethodInvocation: public void fr.gregoire.passpro.backend.controller.DistributorsController.test() throws java.lang.Exception; target is of class [fr.gregoire.passpro.backend.controller.DistributorsController]
2023-01-10 10:16:48,525 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver : Using #ExceptionHandler fr.gregoire.passpro.backend.resterror.RestExceptionHandler#handleAuthenticationException(AuthenticationException)
2023-01-10 10:16:48,526 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.annotation.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json]
2023-01-10 10:16:48,526 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.annotation.HttpEntityMethodProcessor : Writing [fr.gregoire.passpro.backend.resterror.RestErrors#55eb7689]
2023-01-10 10:16:48,527 [http-nio-8080-exec-1] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext]
2023-01-10 10:16:48,528 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet : Completed 401 UNAUTHORIZED
Quick info
spring boot 3
spring security 6
Code
package fr.gregoire.passpro.backend.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
#Configuration
#EnableWebSecurity(debug = true)
#EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {
#Autowired
UserDetailsService userDetailsService;
#Bean
public SecurityContextRepository securityContextRepository() {
return new HttpSessionSecurityContextRepository();
}
#Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) throws Exception {
return new ProviderManager(daoAuthenticationProvider());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName("continue");
// #formatter:off
http
.headers().frameOptions().disable().and()
.cors().and()
.csrf().disable()
.formLogin().disable()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)).and()
.securityMatcher("/api/*/auth/**")
.authorizeHttpRequests(authz -> authz.requestMatchers("/api/*/auth/**").permitAll()
.requestMatchers("/api/v1/auth/me").permitAll()
.requestMatchers("/api/*/public/**").permitAll()
.requestMatchers("/api/*/catalogs/*/documents/*/file").permitAll()
.requestMatchers(req -> req.getRequestURI().contains("mail-images")).permitAll()
.requestMatchers(req -> req.getRequestURI().contains("api-docs")).permitAll()
.requestMatchers(req -> req.getRequestURI().contains("swagger-ui")).permitAll()
.requestMatchers(req -> req.getRequestURI().contains("h2-console")).permitAll()
.anyRequest().authenticated())
.authenticationProvider(daoAuthenticationProvider())
.securityContext((securityContext) -> securityContext.requireExplicitSave(false)) // Spring secu 5.8
.securityContext((securityContext) -> securityContext.securityContextRepository(new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
securityContextRepository()
)))
;
// #formatter:on
return http.build();
}
}
package fr.gregoire.passpro.backend.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import fr.gregoire.passpro.backend.dto.LoginDto;
import fr.gregoire.passpro.backend.dto.UserDto;
import fr.gregoire.passpro.backend.exception.NotFoundException;
import fr.gregoire.passpro.backend.mapper.UserDtoMapper;
import fr.gregoire.passpro.backend.model.UserEntity;
import fr.gregoire.passpro.backend.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
#Validated
#RestController
#RequestMapping(path = "/api/v1/auth", produces = MediaType.APPLICATION_JSON_VALUE)
public class AuthenticationController {
#Autowired
private SecurityContextRepository securityContextRepository;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserService userService;
#Autowired
private UserDtoMapper userDtoMapper;
#PostMapping("/login")
public ResponseEntity<UserDto> login(#NotNull #Valid #RequestBody LoginDto loginDto, HttpServletRequest request, HttpServletResponse response) {
final Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginDto.getLogin(), loginDto.getPassword()));
SecurityContextHolder.getContext()
.setAuthentication(authentication);
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
return ResponseEntity.ok(this.userDtoMapper.modelToDto((UserEntity) authentication.getPrincipal()));
}
#PostMapping("/logout")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void logout() {
SecurityContextHolder.clearContext();
}
#GetMapping("/me")
public ResponseEntity<UserDto> getAuthenticatedUser() throws NotFoundException {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (null == authentication || !authentication.isAuthenticated() || !(authentication.getPrincipal() instanceof UserEntity)) {
throw new SessionAuthenticationException("UNAUTHORIZED");
}
return ResponseEntity.ok(this.userDtoMapper.modelToDto(this.userService.findById(((UserEntity) authentication.getPrincipal()).getId())));
}
}
#Validated
#RestController
#RequestMapping(path = "/api/v1/distributors", produces = MediaType.APPLICATION_JSON_VALUE)
public class DistributorsController {
#GetMapping("/isauthenticated")
#Secured(RoleEnumConstants.ROLE_ADMIN)
public void test() throws Exception {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
System.out.println(authentication.getClass()
.getCanonicalName());
System.out.println(authentication.getCredentials());
System.out.println(authentication.isAuthenticated());
System.out.println(authentication.getDetails());
System.out.println(authentication.getAuthorities());
}
}
Thanks for your help and have a nice day,
Nicolas
Here is the full stacktrace AuthenticationCredentialsNotFoundException thrown at step 3 (when I tried to access to http://localhost:8080/api/v1/distributors/isauthenticated as admin)
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.lambda$getAuthentication$3(AuthorizationManagerBeforeMethodInterceptor.java:266)
at org.springframework.security.authorization.AuthorityAuthorizationManager.check(AuthorityAuthorizationManager.java:142)
at org.springframework.security.authorization.method.SecuredAuthorizationManager.check(SecuredAuthorizationManager.java:55)
at org.springframework.security.authorization.method.SecuredAuthorizationManager.check(SecuredAuthorizationManager.java:40)
at org.springframework.security.config.annotation.method.configuration.DeferringObservationAuthorizationManager.check(DeferringObservationAuthorizationManager.java:47)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:252)
at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
at fr.gregoire.passpro.backend.controller.DistributorsController$$SpringCGLIB$$0.test(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1010)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:902)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:705)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:884)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at fr.gregoire.passpro.backend.config.JwtAuthFilter.doFilterInternal(JwtAuthFilter.java:36)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:66)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:219)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)
at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:90)
at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:78)
at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:67)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:351)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
2023-01-10 11:35:33,537 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.annotation.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json]
2023-01-10 11:35:33,537 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.annotation.HttpEntityMethodProcessor : Writing [fr.gregoire.passpro.backend.resterror.RestErrors#31570bd6]
2023-01-10 11:35:33,538 [http-nio-8080-exec-2] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext]
2023-01-10 11:35:33,538 [http-nio-8080-exec-2] DEBUG org.springframework.web.servlet.DispatcherServlet : Completed 401 UNAUTHORIZED
I have a problem about opening swagger ui in my Spring Boot Example.
I get this kind of error when I access to localhost:8080/swagger-ui or localhost:8080/root-api-name:swagger-ui
Securing GET /springboot-blog-rest-api/swagger-ui
2022-07-22 01:38:58.820 DEBUG 30576 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-07-22 01:38:58.820 DEBUG 30576 --- [nio-8080-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2022-07-22 01:38:58.820 DEBUG 30576 --- [nio-8080-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /springboot-blog-rest-api/swagger-ui] with attributes [authenticated]
2022-07-22 01:38:58.820 DEBUG 30576 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-07-22 01:38:58.820 DEBUG 30576 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Securing GET /error
2022-07-22 01:38:58.821 DEBUG 30576 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-07-22 01:38:58.821 DEBUG 30576 --- [nio-8080-exec-4] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2022-07-22 01:38:58.821 DEBUG 30576 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Secured GET /error
2022-07-22 01:38:58.821 DEBUG 30576 --- [nio-8080-exec-4] a.DefaultWebInvocationPrivilegeEvaluator : filter invocation [/error] denied for AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]
org.springframework.security.access.AccessDeniedException: Access is denied
I used springfox-swagger2 version 3 , springfox-boot-starter version 3 and lastly springfox-swagger-ui version 2.9.
How can I fix my issue?
Here is my SwaggerConfig File.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
private ApiKey apiKey(){
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
private ApiInfo apiInfo(){
return new ApiInfo(
"Spring Boot Blog REST APIs",
"Spring Boot Blog REST API Documentation",
"1",
"Terms of service",
new Contact("Name", "website-address", "Email"),
"License of API",
"API license URL",
Collections.emptyList()
);
}
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext()))
.securitySchemes(Arrays.asList(apiKey()))
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private SecurityContext securityContext(){
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}
private List<SecurityReference> defaultAuth(){
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}
}
Here is my SecurityConfig file
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(){
return new JwtAuthenticationFilter();
}
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/v2/api-docs/**",
"/swagger-ui/**","/swagger-resources/**","/swagger-ui.html","/webjars/**");
}
#Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests((authorize) -> authorize
.antMatchers(HttpMethod.GET, "/api/v1/**").permitAll()
.antMatchers("/api/v1/auth/**").permitAll()
.anyRequest()
.authenticated()
);
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
#Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
Here is the answer
I can access the url through localhost:8080/swagger-ui.html
If you are using Spring Security, use the following security configuration to allow public access to Swagger UI resources:
public class SecurityConfig {
private static final String[] AUTH_WHITELIST = {
// for Swagger UI v2
"/v2/api-docs",
"/swagger-ui.html",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/webjars/**",
// for Swagger UI v3 (OpenAPI)
"/v3/api-docs/**",
"/swagger-ui/**"
};
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) {
httpSecurity
// ... other configuration settings
.antMatchers(AUTH_WHITELIST).permitAll();
// ...
}
}
I am getting a "Circular View Path Error" but I don't know why. If I am understanding it correctly this error occurs when you are stuck in a infinite loop. But I don't see an infinite Loop in my code. I googled the error, and tested every solution I found, but nothing worked.
MyUserDetails:
package de.gabriel.vertretungsplan.models;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class MyUserDetails implements UserDetails {
private String userName;
private String password;
private boolean active;
private List<GrantedAuthority> authorities;
public MyUserDetails(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.active = user.isActive();
this.authorities = Arrays.stream(user.getRoles().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
System.out.println(userName);
System.out.println(password);
System.out.println(active);
System.out.println(authorities);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return userName;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return active;
}
}
User:
package de.gabriel.vertretungsplan.models;
import javax.persistence.*;
#Entity
#Table(name = "lehrer", schema = "public")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String userName;
private String password;
private boolean active;
private String roles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
}
Rest Controller/ HomeResource:
package de.gabriel.vertretungsplan;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HomeResource {
#GetMapping("/")
public String home(){
return ("<h1>Hier wird der Vertretungsplan einsehbar sein</h1>");
}
#GetMapping("/lehrer")
public String lehrer(){
return ("<h1>Hier wird das Formular einsehbar sein</h1>");
}
#GetMapping("/admin")
public String admin(){
return ("<h1>Hier wird die Admin Übersicht einsehbar sein</h1>");
}
}
MyUserDetailsService:
package de.gabriel.vertretungsplan;
import de.gabriel.vertretungsplan.models.MyUserDetails;
import de.gabriel.vertretungsplan.models.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Optional;
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUserName(userName);
user.orElseThrow(() -> new UsernameNotFoundException("Username " + userName + " not found!"));
return user.map(MyUserDetails::new).get();
}
}
User Repository:
package de.gabriel.vertretungsplan;
import de.gabriel.vertretungsplan.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUserName(String userName);
}
Security Configuration:
package de.gabriel.vertretungsplan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/lehrer").hasAnyRole("ADMIN","LEHRER")
.antMatchers("/").permitAll()
.and().formLogin();
}
#Bean
public PasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
And finally, this is my main:
package de.gabriel.vertretungsplan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class VertretungsplanApplication {
public static void main(String[] args) {
SpringApplication.run(VertretungsplanApplication.class, args);
}
}
I don't think, the errors origin can be in the database, but if it is, here is the contents of the "lehrer" table in the database "vertretungsplan":
Here is the error log:
2022-04-17 00:50:57.562 ERROR 14740 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
javax.servlet.ServletException: Circular view path [error]: would dispatch back to the current handler URL [/error] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:210) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:148) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1401) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.18.jar:5.3.18]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.18.jar:5.3.18]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.18.jar:5.3.18]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:80) ~[spring-boot-2.6.6.jar:2.6.6]
at org.springframework.boot.web.servlet.filter.ErrorPageSecurityFilter.doFilter(ErrorPageSecurityFilter.java:70) ~[spring-boot-2.6.6.jar:2.6.6]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:87) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:237) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:223) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-5.3.18.jar:5.3.18]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.18.jar:5.3.18]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) ~[spring-web-5.3.18.jar:5.3.18]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:403) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:249) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Here are the debug logs(they are too long, so I posted the longs of when the error happend):
2022-04-17 00:58:48.395 DEBUG 12924 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Securing POST /login
2022-04-17 00:58:48.395 DEBUG 12924 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
Hibernate:
select
user0_.id as id1_0_,
user0_.active as active2_0_,
user0_.password as password3_0_,
user0_.roles as roles4_0_,
user0_.user_name as user_nam5_0_
from
public.lehrer user0_
where
user0_.user_name=?
admin
$2a$12$E8STVE26N/QpoL90VfoOAuFRnYvbfzHKlbGRe.KVOMYCqKtDKNK2G
true
[ADMIN]
2022-04-17 00:58:48.796 DEBUG 12924 --- [nio-8080-exec-4] o.s.s.a.dao.DaoAuthenticationProvider : Authenticated user
2022-04-17 00:58:48.797 DEBUG 12924 --- [nio-8080-exec-4] .s.ChangeSessionIdAuthenticationStrategy : Changed session id from CD2D5F40C901F8A7CF32826E28A596C9
2022-04-17 00:58:48.797 DEBUG 12924 --- [nio-8080-exec-4] o.s.s.w.csrf.CsrfAuthenticationStrategy : Replaced CSRF Token
2022-04-17 00:58:48.798 DEBUG 12924 --- [nio-8080-exec-4] w.a.UsernamePasswordAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=de.gabriel.vertretungsplan.models.MyUserDetails#69c46690, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=CD2D5F40C901F8A7CF32826E28A596C9], Granted Authorities=[ADMIN]]
2022-04-17 00:58:48.798 DEBUG 12924 --- [nio-8080-exec-4] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/admin
2022-04-17 00:58:48.798 DEBUG 12924 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=de.gabriel.vertretungsplan.models.MyUserDetails#69c46690, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=CD2D5F40C901F8A7CF32826E28A596C9], Granted Authorities=[ADMIN]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade#35a28abb]
2022-04-17 00:58:48.798 DEBUG 12924 --- [nio-8080-exec-4] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=de.gabriel.vertretungsplan.models.MyUserDetails#69c46690, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=CD2D5F40C901F8A7CF32826E28A596C9], Granted Authorities=[ADMIN]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade#35a28abb]
2022-04-17 00:58:48.798 DEBUG 12924 --- [nio-8080-exec-4] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-04-17 00:58:48.801 INFO 12924 --- [nio-8080-exec-6] Spring Security Debugger :
************************************************************
Request received for GET '/admin':
org.apache.catalina.connector.RequestFacade#328dbcd1
servletPath:/admin
pathInfo:null
headers:
host: localhost:8080
connection: keep-alive
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: same-origin
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: http://localhost:8080/login
accept-encoding: gzip, deflate, br
accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
cookie: Webstorm-bca5ba46=692b9f6e-b03a-4cb4-8435-ea3fef371fc4; JSESSIONID=A90C1A49C3CF4D7943EE0ABBD989B1D4
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
2022-04-17 00:58:48.802 DEBUG 12924 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy : Securing GET /admin
2022-04-17 00:58:48.802 DEBUG 12924 --- [nio-8080-exec-6] w.c.HttpSessionSecurityContextRepository : Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=de.gabriel.vertretungsplan.models.MyUserDetails#69c46690, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=CD2D5F40C901F8A7CF32826E28A596C9], Granted Authorities=[ADMIN]]]
2022-04-17 00:58:48.802 DEBUG 12924 --- [nio-8080-exec-6] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=de.gabriel.vertretungsplan.models.MyUserDetails#69c46690, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=CD2D5F40C901F8A7CF32826E28A596C9], Granted Authorities=[ADMIN]]]
2022-04-17 00:58:48.802 DEBUG 12924 --- [nio-8080-exec-6] o.s.s.w.s.HttpSessionRequestCache : Loaded matching saved request http://localhost:8080/admin
2022-04-17 00:58:48.803 DEBUG 12924 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /admin] with attributes [hasRole('ROLE_ADMIN')]
2022-04-17 00:58:48.803 DEBUG 12924 --- [nio-8080-exec-6] o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code
2022-04-17 00:58:48.803 DEBUG 12924 --- [nio-8080-exec-6] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-04-17 00:58:48.805 INFO 12924 --- [nio-8080-exec-6] Spring Security Debugger :
************************************************************
Request received for GET '/error':
org.apache.catalina.core.ApplicationHttpRequest#1f38c9b6
servletPath:/error
pathInfo:null
headers:
host: localhost:8080
connection: keep-alive
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: same-origin
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: http://localhost:8080/login
accept-encoding: gzip, deflate, br
accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
cookie: Webstorm-bca5ba46=692b9f6e-b03a-4cb4-8435-ea3fef371fc4; JSESSIONID=A90C1A49C3CF4D7943EE0ABBD989B1D4
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
************************************************************
You get this error, because you did not fully configure Spring MVC, namely error handling part (and probably others).
Chain of events is as follows:
Call API and get 403 forbidden resource
2022-04-17 00:58:48.803 DEBUG 12924 --- [nio-8080-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor : Failed to authorize filter invocation [GET /admin] with attributes [hasRole('ROLE_ADMIN')] 2022-04-17 00:58:48.803 DEBUG 12924 --- [nio-8080-exec-6] o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code
Spring redirects to /error view:
Request received for GET '/error': org.apache.catalina.core.ApplicationHttpRequest#1f38c9b6 servletPath:/error
Here something happens (it was not found, or something is missing), it leads to another error -> and here is when exception happens:
javax.servlet.ServletException: Circular view path [error]: would dispatch back to the current handler URL [/error] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Solution:
Try to search by "springboot mvc set up white lable error page".
And also check why you get 403 status in the first place, as according to logs, you logged in as admin user. Please, read about spring security adding ROLE_ prefix. Most probably, in the DB it has to be ROLE_ADMIN and in the code hasRole('ADMIN')
Hope, it helped
When I POST to /api/v1/auth/register, I get a 403 response generated by the configured accessDeniedHandler. However, I'm expecting this request to be permitted as it's covered by permitAll() and precedes anyRequest().authenticated().
Requests such as GET /api/v1/reference/countries work just fine. Also, my integration tests that hit these /api/v1/auth/** endpoints also work, which suggests something to do with CORS, although the preflight request is 200.
Any idea what's wrong with this security configuration?
#Configuration
#EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
#Order(1)
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsService;
private final Config config;
private final ObjectMapper objectMapper;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.authorizeRequests()
.antMatchers(
"/api/v1/auth/register",
"/api/v1/auth/register/check",
"/api/v1/auth/register/activate",
"/api/v1/auth/password/update",
"/api/v1/auth/recover",
"/api/v1/auth/recover/check",
"/api/v1/auth/recover/reset",
"/api/v1/csrf-token",
"/api/v1/reference/**"
)
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.successHandler((request, response, authentication) -> {
response.getWriter().append("OK");
response.setStatus(HttpServletResponse.SC_OK);
})
.failureHandler((request, response, exception) -> {
response.getWriter().append("Invalid credentials or inactive account");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
})
.loginProcessingUrl("/api/v1/auth/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/v1/auth/logout", "POST"))
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler((request, response, accessDeniedException) -> {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
objectMapper.writeValue(
response.getWriter(),
ErrorResponseBody
.builder()
.code(ErrorType.ACCESS_DENIED)
.status(HttpServletResponse.SC_FORBIDDEN)
.message("Access denied")
.build()
);
})
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
objectMapper.writeValue(
response.getWriter(),
ErrorResponseBody
.builder()
.code(ErrorType.LOGIN_REQUIRED)
.status(HttpServletResponse.SC_UNAUTHORIZED)
.message("You are not authorized to access this resource")
.build()
);
})
.and()
.userDetailsService(userDetailsService);
if (config.isCsrfDisabled()) {
http
.csrf()
.disable();
}
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final var configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(config.getAllowedOrigins());
configuration.setAllowedMethods(asList("GET", "POST", "PUT", "PATCH", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList(HttpHeaders.AUTHORIZATION, HttpHeaders.CACHE_CONTROL, HttpHeaders.CONTENT_TYPE, HttpHeaders.ACCEPT));
final var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
}
Here's my CORS config:
#Configuration
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class WebConfig {
private final Config config;
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
final var allowedOrigins = Optional
.ofNullable(config.getAllowedOrigins())
.map(origins -> origins.toArray(new String[]{}))
.orElse(new String[]{});
System.out.println("Enabling CORS for the following origins:" + Arrays.asList(allowedOrigins).toString());
registry
.addMapping("/api/**")
.allowedOrigins(allowedOrigins)
.allowCredentials(true)
.allowedMethods("*")
.allowedHeaders("*");
}
};
}
}
I'm invoking these endpoints from http://localhost:3000, which is one of the items returned by config.getAllowedOrigins().
Here's the Spring Security debug logging from the request:
2020-11-24 06:51:16.110 INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-11-24 06:51:16.112 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-11-24 06:51:16.158 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 44 ms
2020-11-24 06:51:16.193 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-24 06:51:16.200 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-24 06:51:16.205 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-24 06:51:16.206 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-24 06:51:16.214 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-24 06:51:16.217 DEBUG 1 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-24 06:51:16.286 DEBUG 1 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#461bbc94
2020-11-24 06:51:16.290 DEBUG 1 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-11-24 06:51:16.299 DEBUG 1 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-11-24 06:51:16.483 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-11-24 06:51:16.485 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-11-24 06:51:16.486 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2020-11-24 06:51:16.487 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-11-24 06:51:16.488 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-11-24 06:51:16.489 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 4 of 13 in additional filter chain; firing Filter: 'CorsFilter'
2020-11-24 06:51:16.494 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/auth/register at position 5 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-11-24 06:51:16.525 DEBUG 1 --- [nio-8080-exec-2] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://dev.api.example.com/api/v1/auth/register
2020-11-24 06:51:16.609 DEBUG 1 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#461bbc94
2020-11-24 06:51:16.610 DEBUG 1 --- [nio-8080-exec-2] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-11-24 06:51:16.615 DEBUG 1 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
Disable CSRF using .csrf().disable() in configure(HttpSecurity http) More deatils are explained here
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
... // other configurations
}
I am trying to set up a java configurated spring environment including spring security. The application starts without any error, but I am not able to login succeffully.
WebAppInitializer
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
#Order(value = 1)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { HibernateConfig.class, SecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
SecurityInitializer
import org.springframework.core.annotation.Order;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
#Order(value = 2)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
SecurityConfig
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/webjars/**","/css/**","/img/**");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(getUserQuery())
.authoritiesByUsernameQuery(getAuthoritiesQuery());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasAnyAuthority("EMPLOYEE", "TEAM_LEADER", "ADMIN")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/success-login", true)
.loginProcessingUrl("/process-login")
.failureUrl("/error-login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.permitAll()
.and()
.rememberMe()
.and()
.csrf()
.disable();
}
private String getUserQuery() {
return "SELECT e_mail as username, password as password, active as enabled "
+ "FROM employee "
+ "WHERE e_mail = ?";
}
private String getAuthoritiesQuery() {
return "SELECT DISTINCT employee.e_mail as username, role.name as authority "
+ "FROM employee, employee_role, role "
+ "WHERE employee.id = employee_role.employee_id "
+ "AND role.id = employee_role.role_id "
+ "AND employee.e_mail = ? "
+ "AND employee.active = 1";
}
}
login.jsp
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<div class="login-container">
<form class="form-signin" method="POST" action="<c:url value='/process-login'/>">
<h2 class="form-signin-heading">Anmelden</h2>
<c:if test="${error == true}">
<div class="alert alert-danger">
<a class="close" data-dismiss="alert" href="#">×</a>
<p>Login fehlgeschlagen: Eingegebener Nutzername oder Passwort ist falsch.</p>
</div>
</c:if>
<input type="text" name="j_username" id="j_username"
class="form-control" placeholder="eMail Adresse" required autofocus>
<input type="password" name="j_password" id="j_password"
class="form-control" placeholder="Passwort" required> <label
class="checkbox"> <input type="checkbox" value="remember-me">
Angemeldet bleiben
</label>
<button class="btn btn-lg btn-primary btn-block" type="submit">Anmelden</button>
</form>
</div>
LoginController
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class LoginController {
#RequestMapping(value={"/login"}, method=RequestMethod.GET)
public ModelAndView showLoginPage() {
return new ModelAndView("login/login");
}
#RequestMapping(value="/success-login", method=RequestMethod.POST)
public ModelAndView successLogin() {
return new ModelAndView("/dashboard");
}
#RequestMapping(value="/process-login", method=RequestMethod.POST)
public ModelAndView processLogin() {
return new ModelAndView("/dashboard");
}
#RequestMapping(value="/error-login", method=RequestMethod.GET)
public ModelAndView invalidLogin() {
ModelAndView modelAndView = new ModelAndView("login/login");
modelAndView.addObject("error", true);
return modelAndView;
}
#RequestMapping(value="/logout", method=RequestMethod.POST)
public ModelAndView logout() {
return new ModelAndView("login/login");
}
}
Database:
Content of Role Table: "EMPLOYEE", "TEAM_LEADER", "ADMIN",
I am looking for a solution a long time, but unfortunately I can not find the error. I appreciate all your advices.
Update 1 - Log4J Log after one login
2013-11-19 11:17:47,223 [DEBUG] [AbstractSecurityInterceptor.java : 310] org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9056f12c: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 45D43FE758F35164E5FE7BC9D08F81B8; Granted Authorities: ROLE_ANONYMOUS
2013-11-19 11:17:47,231 [DEBUG] [AffirmativeBased.java : 65] org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#2f7b1445, returned: -1
2013-11-19 11:17:47,237 [DEBUG] [ExceptionTranslationFilter.java : 165] org.springframework.security.web.access.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Zugriff verweigert
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:139)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
2013-11-19 11:17:47,239 [DEBUG] [AndRequestMatcher.java : 66] org.springframework.security.web.util.matcher.AndRequestMatcher - Trying to match using Ant [pattern='/**', GET]
2013-11-19 11:17:47,239 [DEBUG] [AntPathRequestMatcher.java : 127] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'POST /j_spring_security_check' doesn't match 'GET /**
2013-11-19 11:17:47,240 [DEBUG] [AndRequestMatcher.java : 69] org.springframework.security.web.util.matcher.AndRequestMatcher - Did not match
2013-11-19 11:17:47,240 [DEBUG] [HttpSessionRequestCache.java : 44] org.springframework.security.web.savedrequest.HttpSessionRequestCache - Request not saved as configured RequestMatcher did not match
2013-11-19 11:17:47,240 [DEBUG] [ExceptionTranslationFilter.java : 185] org.springframework.security.web.access.ExceptionTranslationFilter - Calling Authentication entry point.
2013-11-19 11:17:47,241 [DEBUG] [DefaultRedirectStrategy.java : 36] org.springframework.security.web.DefaultRedirectStrategy - Redirecting to 'http://localhost:8080/holidayplanner/login'
2013-11-19 11:17:47,241 [DEBUG] [HttpSessionSecurityContextRepository.java : 300] org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2013-11-19 11:17:47,242 [DEBUG] [SecurityContextPersistenceFilter.java : 97] org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
2013-11-19 11:17:47,246 [DEBUG] [AntPathRequestMatcher.java : 145] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/webjars/**'
2013-11-19 11:17:47,246 [DEBUG] [AntPathRequestMatcher.java : 145] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/css/**'
2013-11-19 11:17:47,246 [DEBUG] [AntPathRequestMatcher.java : 145] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/img/**'
2013-11-19 11:17:47,247 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2013-11-19 11:17:47,247 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2013-11-19 11:17:47,247 [DEBUG] [HttpSessionSecurityContextRepository.java : 148] org.springframework.security.web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
2013-11-19 11:17:47,247 [DEBUG] [HttpSessionSecurityContextRepository.java : 90] org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#2664e105. A new one will be created.
2013-11-19 11:17:47,247 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2013-11-19 11:17:47,247 [DEBUG] [HstsHeaderWriter.java : 129] org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#2f389f24
2013-11-19 11:17:47,247 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 4 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
2013-11-19 11:17:47,247 [DEBUG] [AntPathRequestMatcher.java : 145] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/login'; against '/logout'
2013-11-19 11:17:47,248 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 5 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2013-11-19 11:17:47,248 [DEBUG] [AntPathRequestMatcher.java : 127] org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /login' doesn't match 'POST /success-login
2013-11-19 11:17:47,248 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 6 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2013-11-19 11:17:47,248 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 7 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2013-11-19 11:17:47,248 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 8 of 12 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
2013-11-19 11:17:47,248 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2013-11-19 11:17:47,248 [DEBUG] [AnonymousAuthenticationFilter.java : 102] org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9056f12c: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 45D43FE758F35164E5FE7BC9D08F81B8; Granted Authorities: ROLE_ANONYMOUS'
2013-11-19 11:17:47,249 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2013-11-19 11:17:47,249 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2013-11-19 11:17:47,249 [DEBUG] [FilterChainProxy.java : 337] org.springframework.security.web.FilterChainProxy - /login at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2013-11-19 11:17:47,249 [DEBUG] [AbstractSecurityInterceptor.java : 194] org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /login; Attributes: [permitAll]
2013-11-19 11:17:47,249 [DEBUG] [AbstractSecurityInterceptor.java : 310] org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9056f12c: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#380f4: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 45D43FE758F35164E5FE7BC9D08F81B8; Granted Authorities: ROLE_ANONYMOUS
2013-11-19 11:17:47,249 [DEBUG] [AffirmativeBased.java : 65] org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#2f7b1445, returned: 1
2013-11-19 11:17:47,249 [DEBUG] [AbstractSecurityInterceptor.java : 215] org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Authorization successful
2013-11-19 11:17:47,250 [DEBUG] [AbstractSecurityInterceptor.java : 227] org.springframework.security.web.access.intercept.FilterSecurityInterceptor - RunAsManager did not change Authentication object
2013-11-19 11:17:47,250 [DEBUG] [FilterChainProxy.java : 323] org.springframework.security.web.FilterChainProxy - /login reached end of additional filter chain; proceeding with original chain
2013-11-19 11:17:47,254 [DEBUG] [ExceptionTranslationFilter.java : 115] org.springframework.security.web.access.ExceptionTranslationFilter - Chain processed normally
2013-11-19 11:17:47,254 [DEBUG] [HttpSessionSecurityContextRepository.java : 300] org.springframework.security.web.context.HttpSessionSecurityContextRepository - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2013-11-19 11:17:47,254 [DEBUG] [SecurityContextPersistenceFilter.java : 97] org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
Working Solution:
Finally I found a working solution with your help. Thanks for this. Following all needed Files if someone other is interested in a java configured spring security:
Login JSP:
<div class="login-container">
<form class="form-signin navbar navbar-default" method="POST" action="<c:url value='/login/process-login'/>">
<div class="form-signin-img">
<span class="form-signin-img-helper"></span>
<img src="<c:url value='/img/itensis_logo.gif'/>" />
</div>
<h2 class="form-signin-heading">${msg_heading}</h2>
<c:if test="${error == true}">
<div class="alert alert-danger">
<button type="button" class="close close-box">×</button>
<p>${msg_error}</p>
</div>
</c:if>
<input type="text" name="security_username" id="security_username" class="form-control" placeholder="${msg_username}" required autofocus>
<input type="password" name="security_password" id="security_password" class="form-control" placeholder="${msg_password}" required>
<label class="checkbox">
<input type="checkbox" name="remember_me_checkbox"> ${msg_rememberMe}
</label>
<button class="btn btn-lg btn-primary btn-block" type="submit">
<i class="fa fa-sign-in fa-lg"></i>
<span>${msg_login}</span>
</button>
</form>
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired private UserDetailsService userDetailService;
#Autowired private DataSource dataSource;
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/js/**","/css/**","/img/**","/webjars/**","/pages/**");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
ShaPasswordEncoder shaPasswordEncoder = new ShaPasswordEncoder(256);
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(getUserQuery())
.authoritiesByUsernameQuery(getAuthoritiesQuery())
.passwordEncoder(shaPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasAuthority("BASIC_PERMISSION")
.and()
.formLogin()
.loginPage("/login/login")
.defaultSuccessUrl("/login/success-login", true)
.failureUrl("/login/error-login")
.loginProcessingUrl("/login/process-login")
.usernameParameter("security_username")
.passwordParameter("security_password")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login/login")
.logoutUrl("/login/logout")
.permitAll()
.and()
.rememberMe()
.key("your_key")
.rememberMeServices(rememberMeServices())
.and()
.csrf()
.disable();
}
#Bean
public RememberMeServices rememberMeServices() {
// Key must be equal to rememberMe().key()
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("your_key", userDetailService);
rememberMeServices.setCookieName("remember_me_cookie");
rememberMeServices.setParameter("remember_me_checkbox");
rememberMeServices.setTokenValiditySeconds(2678400); // 1month
return rememberMeServices;
}
#Bean
public UserDetailsService userDetailService() {
return new SecurityUserDetailService();
}
private String getUserQuery() {
return "SELECT username as username, password as password, active as enabled "
+ "FROM employee "
+ "WHERE username = ?";
}
private String getAuthoritiesQuery() {
return "SELECT DISTINCT employee.username as username, permission.name as authority "
+ "FROM employee, employee_role, role, role_permission, permission "
+ "WHERE employee.id = employee_role.employee_id "
+ "AND role.id = employee_role.role_id "
+ "AND role.id = role_permission.role_id "
+ "AND permission.id = role_permission.permission_id "
+ "AND employee.username = ? "
+ "AND employee.active = 1";
}
}
LoginController.java
#Controller
#RequestMapping("/login")
public class LoginController {
#RequestMapping(value={"/login"}, method=RequestMethod.GET)
public ModelAndView showLoginPage() {
return new ModelAndView("loginForm");
}
#RequestMapping(value="/success-login", method=RequestMethod.GET)
public String successLogin() {
return "forward:/dashboard/dashboard";
}
#RequestMapping(value="/error-login", method=RequestMethod.GET)
public ModelAndView invalidLogin() {
ModelAndView modelAndView = new ModelAndView("loginForm");
modelAndView.addObject("error", true);
return modelAndView;
}
#RequestMapping(value="/logout", method=RequestMethod.POST)
public ModelAndView logout() {
return new ModelAndView("dashboardForm");
}
}
Update 1: SecurityUserDetailService
#Service
#Transactional
public class SecurityUserDetailService implements UserDetailsService {
#Autowired private EmployeeService employeeService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
Employee loggedInEmployee = employeeService.findEmployeeByUsername(username);
List<GrantedAuthority> authorities = getAuthorities(loggedInEmployee);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
loggedInEmployee.getUsername(),
loggedInEmployee.getPassword().toLowerCase(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
authorities);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static List<GrantedAuthority> getAuthorities(Employee employee) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : employee.getRoles()) {
for (Permission permission : role.getPermissions()) {
authorities.add(new SimpleGrantedAuthority(permission.getName()));
}
}
return authorities;
}
}