spring security + jwt loadUserByUsername not call - java

[SecurityConfig]
#Slf4j
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
/*.headers().disable()*/
.anyRequest().permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(authenticationManager(authenticationConfiguration)), UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
[JwtAuthenticationFilter]
#RequiredArgsConstructor
#Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
log.info("Check Run");
ObjectMapper om = new ObjectMapper();
try {
AdvertiserUserDto.Read read = om.readValue(request.getInputStream(), AdvertiserUserDto.Read.class);
UsernamePasswordAuthenticationToken authenticationToken
= new UsernamePasswordAuthenticationToken(read.getUserLoginId(), read.getPassword());
log.info(authenticationToken.getPrincipal().toString());
log.info(authenticationToken.getCredentials().toString());
return authenticationManager.authenticate(authenticationToken);
}
catch (IOException e) {
e.printStackTrace();
log.error("IO Exception");
}
return null;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
log.info("Success");
super.successfulAuthentication(request, response, chain, authResult);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
log.info("Fail");
super.unsuccessfulAuthentication(request, response, failed);
}
}
[SecurityUserDetailsService]
#Slf4j
#RequiredArgsConstructor
#Service
public class SecurityUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
#Transactional
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("loadUserByUsername"); // **not Run**
UserEntity user = userRepository.findByUserLoginId(username).orElseThrow(() -> new UsernameNotFoundException("no Search User"));
return new SecurityUserDetails(
user,
/*Collections.singleton(new SimpleGrantedAuthority("ROLE_" + user.getType().getRoll()))*/
Collections.singleton(new SimpleGrantedAuthority("ROLE_" + "USER"))
);
}
}
[SecurityUserDetails]
#RequiredArgsConstructor
#Data
public class SecurityUserDetails implements UserDetails {
private final UserEntity user;
private final Collection<? extends GrantedAuthority> authorities;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUserId().toString();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
Please note that I am struggling at a very basic level.
Here's the process I've tried so far:
Login attempt (user, pw)
Security Chain is executed
A filter that inherits from UsernamePasswordAuthenticationFilter is executed
It has been confirmed that ID and PW are normally passed as parameters.
Execute the authenticate() method of AuthenticationManager to check if ID and PW are correct.
The loadUserByUsername() method, which inherited UserDeatilsService, is not executed, and immediately the unsuccessfulAuthentication() method is executed and fails.
I attach the code.
According to my plan, validation should be done via loadUserByUsername().
But why does the validation job fail immediately without even being executed?
Result :
c.b.k.c.c.t.JwtAuthenticationFilter : Check Run
c.b.k.c.c.t.JwtAuthenticationFilter : test
c.b.k.c.c.t.JwtAuthenticationFilter : 1234
c.b.k.c.c.t.JwtAuthenticationFilter : Fail

I answer myself how I found it.
#Slf4j
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig {
private final SecurityUserDetailsService securityUserDetailsService;
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(securityUserDetailsService);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
httpSecurity
.cors()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(authenticationManager)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
/*.headers().disable()*/
.anyRequest().permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Here's how I discovered and succeeded by other advice.
I think there are some things wrong with this method or something that needs to be improved.
I don't know if it's the right way, but I'm writing it down for others and for improvement.
AuthenticationManagerBuilder authenticationManagerBuilder = httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(securityUserDetailsService);
AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
The key is here.
I approached the problem in that the userDetatilsService that I have customized and extended cannot be informed (cannot be injected) to the AuthenticationManager.

Related

Spring security passes complete decoded jwt token to loadUserByUsername method how can I get it to pass only username from the token instead?

I've encoded additional data to jwt token like user authorities,username etc. Spring security is invoking loadUserByUsername with complete jwt token decoded how can I get it to pass only username from the token to userdetailsService?
code
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("myUserDetailsService")
private UserDetailsService myUserDetailsService;
#Autowired
private MessageSource messageSource;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private CustomAuthenticationProvider authProvider;
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider).userDetailsService(myUserDetailsService).passwordEncoder(new PlainTextPasswordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.cors()
.and()
.authorizeRequests()
.antMatchers("/api/authenticate")
.permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);//NOSONAR
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html");
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new CustomAuthenticationEntryPoint(messageSource);
}
}
UserDetailsService
#Service("myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Value("${jwt.password.secret}")
public String password;
#Autowired
AekAztabRepository aekAztabRepository;
#Autowired
ConversionService conversionService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Aektab> aekAztab = aektabRepository.findByGeNr(username);
if(aektab.isPresent()) {
if (aekAztab.get().getGeNr().equals(username)) {
return conversionService.convert(aektab.get(), User.class);
}
}
else{
throw new UsernameNotFoundException("Invalid user name");
}
return null;
}
public List<UserDetails> populateUserDetails()
{
List<UserDetails> userDetails =new ArrayList<>();
return userDetails;
}
}
the username that is passed to loadUserByUsername is username={"username":"123456788","language":null,"deactivationReason":null,"terminationYear":null,"authorities":[{"authority":"ROLE_ADMIN_ROK"}],"active":false,"terminated":false}
how can I override security configurations to get spring security to pass only username from this token ? And which one is called first AuthenticationProvider or UserdetailsService?
update
AuthenticationProvider
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
// use the credentials
// and authenticate against the third-party system
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<>());
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

Spring security authentication server

I am working on authentication service part of cloud app and I created the following security config class.
#Configuration
#EnableWebSecurity
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
private final PasswordEncoder encoder;
private final UserService userService;
private final JwtConstant jwtConstant;
#Autowired
public JwtSecurityConfig(PasswordEncoder encoder, UserService userService, JwtConstant jwtConstant) {
this.encoder= encoder;
this.userService = userService;
this.jwtConstant = jwtConstant;
}
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(encoder);
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(getAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(getAuthenticationFilter())
.authorizeRequests()
.antMatchers(HttpMethod.PUT, "/signup").permitAll()
.anyRequest()
.authenticated();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
return new AuthenticationFilter(authenticationManager(), jwtConstant);
}
}
I am not sure about the chain methods of configure(HttpSecurity http) method. The authentication service will only receive "login" and "signup" requests.
Should I remove authorizeRequests() method as I do not authorize anything?
I am not sure about anyRequest().authenticated() part either if I really need it?
there are a couple of things that have to be changed, but first of all, you have to define a method that will provide jwt for each request and every request should provide an AuthRequest object that contains username and password :
#RestController
public class WelcomeController {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private AuthenticationManager authenticationManager;
#PostMapping("/signup")
public String generateToken(#RequestBody AuthRequest authRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUserName(), authRequest.getPassword())
);
} catch (Exception ex) {
throw new Exception("inavalid username/password");
}
return jwtUtil.generateToken(authRequest.getUserName());
}
}
and in the UserDetailsService you can make authentication as below :
#Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("tried to loging : " + username);
if(!Objects.isNull(username) && !"".equals(username)){
Optional<User> user = userRepository.findUserByUserName(username);
System.out.println(user.get());
if(user.isPresent()){
User userParam = user.get();
return new org.springframework.security.core.userdetails.User(userParam.getUserName(),
userParam.getPassword(), new ArrayList<>());
}
}
throw new UsernameNotFoundException("user does not exists or empty !!");
}
}
and for the configuration side :
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final UserDetailsService userDetailsService;
#Autowired
private final JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
for further information, you can follow my Github branch Authnticaition sample

Spring Security: make endpoint controller only accessible with Authorization: Bearer token header

I'm trying to get an endpoint not accessible (503 error?) without Authorization: Bearer token header
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate")
.permitAll()
.antMatchers("/api/admin/**")
.fullyAuthenticated()
.anyRequest().authenticated().and().
exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests();
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
}
#RestController
#CrossOrigin
#RequestMapping("/api/admin")
public class AdminController {
#RequestMapping("/test")
public String testAdmin() {
return "OK; secret test admin";
}
}
however I can access it just fine
What should I change in my configure method?
EDIT:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Autowired
private JwtUtil jwtUtil;
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder
.getContext().getAuthentication() == null) {
UserDetails userDetails = this
.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
It seems that the jwtRequestFilter's doFilterInternal method never runs: I tried setting the breakpoints in the debugger and the execution never stopped there.
EDIT: whole SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
public SecurityConfig(
UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Bean
DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider =
new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
return daoAuthenticationProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/api/login").permitAll()
// all other requests need to be authenticated
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
}
#Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
HTTP response 503 means service unavailable. You should get 401 Unauthorized when token is missing.
#Configuration
#EnableWebSecurity
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());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/login").permitAll()
// all other requests need to be authenticated
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Using AuthenticationEntryPoint.
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -1L;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
I managed to solve it. Turns out the problem was in me not having correct configurations, so the SecurityConfig never even got applied. I fixed it this way:
WebConfig.java:
#Configuration
#ComponentScan("testproject")
#EnableWebMvc
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "testproject",
entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".html");
return bean;
}
#Bean
public UserDetailsService userDetailsService() {
UserDetailsService userDetailsService =
new UserDetailsServiceImpl();
return userDetailsService;
}
}
MyAppInitializer.java (notice the commented out sc.addListener(new ContextLoaderListener(root)); line, it must be like that, otherwise there are errors - the fix was suggested to me in another SO question):
public class MyAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(final ServletContext sc) throws ServletException {
System.out.println("onStartup!");
AnnotationConfigWebApplicationContext root =
new AnnotationConfigWebApplicationContext();
root.register(WebConfig.class);
root.setServletContext(sc);
root.scan("testproject");
//sc.addListener(new ContextLoaderListener(root));
ServletRegistration.Dynamic appServlet =
sc.addServlet("dispatcher", new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
SecurityWebApplicationInitializer.java:
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class, WebConfig.class);
}
}

Web and Http security config with JWT (ignoring login path problem)

I'm making an app with Spring security using JWT and react. However, it is behaving strangely. In the Configure file, which I did according to a tutorial, it says I need to ignore only the login paths. It works, I can login but after I obtain the token the config swears at me saying I can't access the path... I have the token now! How to fix it? My config files:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
#Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
#Value("${jwt.get.token.uri}")
private String authenticationPath;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/map-runner/", "/map-runner/static/**", "/static/**", "/map-runner/login", "/", "/login"
);
}
}
and
#Configuration
public class Config implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
ResourceResolver resolver = new ReactResourceResolver();
registry.addResourceHandler("/**")
.resourceChain(true)
.addResolver(resolver);
}
public class ReactResourceResolver implements ResourceResolver {
private static final String REACT_DIR = "/static/";
private static final String REACT_STATIC_DIR = "static";
private Resource index = new ClassPathResource(REACT_DIR + "index.html");
private List<String> rootStaticFiles = Arrays.asList("favicon.ico",
"asset-manifest.json", "manifest.json", "service-worker.js");
#Override
public Resource resolveResource(HttpServletRequest request, String requestPath,
List<? extends Resource> locations, ResourceResolverChain chain) {
return resolve(requestPath, locations);
}
#Override
public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain) {
Resource resolvedResource = resolve(resourcePath, locations);
if (resolvedResource == null) {
return null;
}
try {
return resolvedResource.getURL().toString();
} catch (IOException e) {
return resolvedResource.getFilename();
}
}
private Resource resolve(String requestPath, List<? extends Resource> locations) {
if (requestPath == null) return null;
if (rootStaticFiles.contains(requestPath)
|| requestPath.startsWith(REACT_STATIC_DIR)) {
return new ClassPathResource(REACT_DIR + requestPath);
} else
return index;
}
}
}
I also use the server.servlet.context-path=/map-runner path in the properties.
The JWT implementation in other files is like in this tutorial repo https://github.com/in28minutes/spring-boot-react-fullstack-examples/tree/master/spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main

Cannot pass AuthenticationManager to custom filter by #Autowired

I'm trying pass filter JWTLoginFilter to WebSecurityConfig WebSecurityConfigurerAdapter by #Autowired annotation. The problem arise when JWTLoginFilter try get AuthenticationManager from WebSecurityConfig.
When I start server, I get this error:
Description:
The dependencies of some of the beans in the application context form
a cycle:
JWTLoginFilter defined in file
[C:\Users\user\workspace\backend\target\classes\pl\dn\schoolsystem\service\jwt\JWTLoginFilter.class]
webSecurityConfig (field
pl.dn.schoolsystem.service.jwt.JWTLoginFilter
pl.dn.schoolsystem.config.WebSecurityConfig.jwtLoginFilter)
error image
I think this circular dependency injection. I got stuck on this and i have no idea how to solve it.
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private UserSecurityService userSecurityService;
#Autowired
JWTLoginFilter jwtLoginFilter;
private static final String Salt = "salt"; // should be protected better
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12, new SecureRandom(Salt.getBytes()));
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
authorizeRequests().antMatchers("/").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
//.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
//UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtLoginFilter,
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
}
}
JWTLoginFilter:
#Component
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter{
#Autowired
UserService userService;
#Autowired
public JWTLoginFilter(#Value("/login") String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException,
IOException, ServletException {
System.out.println("Jestem w JwtLogginFilter.attemptAuthentication -------------------------------------");
AccountCredentials creds = new ObjectMapper()
.readValue(req.getInputStream(), AccountCredentials.class);
User user = userService.findByUsername(creds.getUsername());
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
user.getAuthorities()
)
);
}
#Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
System.out.println("Jestem w JWTLogginFilter.successfulAuthentication -------------------------------------- ");
System.out.println("authResult.getName(): " + authResult.getName());
TokenAuthenticationService.addAuthentication(response, authResult.getName());
}
}
I'm using Spring Boot 1.5.4. Thanks for suggestions
Your WebSecurityConfig explicitly requests JWTLoginFilter to be injected in it, and JWTLoginFilter requests AuthenticationManager to be injected in its constructor. AuthenticationManager is supplied by WebSecurityConfig, so you have a circular dependency.
Remove #Component annotation from JWTLoginFilter and define the filter as a bean in WebSecurityConfig:
#Bean
public JWTLoginFilter jwtLoginFilter() {
return new JWTLoginFilter("/login", authenticationManager());
}
You will probably also need to inject UserService manually in this method (for example, via constructor).
Thanks for help Roman Puchkovskiy I can correct my code. Final result of my code:
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private UserSecurityService userSecurityService;
private static final String Salt = "salt"; // should be protected better
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12, new SecureRandom(Salt.getBytes()));
}
#Bean
public JWTLoginFilter jwtLoginFilter() throws Exception {
return new JWTLoginFilter("/login", authenticationManager());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
authorizeRequests().antMatchers("/").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
// .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
// UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtLoginFilter(),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userSecurityService).passwordEncoder(passwordEncoder());
}
}
JWTLoginFilter:
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter{
#Autowired
UserService userService;
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException,
IOException, ServletException {
System.out.println("Jestem w JwtLogginFilter.attemptAuthentication -------------------------------------");
AccountCredentials creds = new ObjectMapper()
.readValue(req.getInputStream(), AccountCredentials.class);
User user = userService.findByUsername(creds.getUsername());
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
user.getAuthorities()
)
);
}
#Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
System.out.println("Jestem w JWTLogginFilter.successfulAuthentication -------------------------------------- ");
System.out.println("authResult.getName(): " + authResult.getName());
TokenAuthenticationService.addAuthentication(response, authResult);
}
}

Categories

Resources