Reputation: 1275
I got a simple problem for you guys. I face the problem that I always get a 404 response if I implement Spring Security in my Spring Boot application.
I debugged through the entire code and I just find out that the redirect path is always "/" and not the called URL.
For example
call localhost:8080/user/login/
-> Spring Security check given credentials
-> they are correct
-> resolve given path
-> SOMETHING STANGES HAPPENS HERE (Refer to figure (1))
-> resolve path to "/" and not "/user/login/"
-> Therefore I get the response 404 NOT FOUND because it returns the wrong path
Debugged mode -- determineTargetUrl -> this.targetUrlParameter is not set and therefore the targetUrl will be "/" and not the real targetUrl "/user/login/" . The code is shown in figure (1).
figure(1) Spring Secutiry class - AbstractAuthenticationTargetUrlRequestHandler
My Code with Spring Security
WebSecurityConfig
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.disable()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/user/login/").permitAll()
.antMatchers(HttpMethod.GET, "/user/secret/**").permitAll()
.and()
.addFilterBefore(new JWTLoginFilter("/user/login/", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
JWTLoginFilter
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
Optional<User> dbUser = SpringContextBridge.services().getUserRepository().findUserByEmail(user.getEmail());
dbUser.ifPresent(us -> user.setPassword(SecretGenerator.generateSha256(user.getPassword(), us.getSecret())));
return getAuthenticationManager()
.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword(), Collections.emptyList())
);
}
}
JWTAuthenticationFilter
public class JWTAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
UserDetailsServiceImpl
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<User> user = userRepository.findUserByEmail(email);
if (!user.isPresent()) {
throw new UserNotFoundException("User with email: " + email + " not found");
}
return new org.springframework.security.core.userdetails.User(user.get().getEmail(), user.get().getPassword(), Collections.emptyList());
}
}
Maybe someone can help me!
Upvotes: 1
Views: 1120
Reputation: 90
I had a similar problem and the solution that worked for me was to override the successfulAuthentication method of AbstractAuthenticationProcessingFilter like this
override fun successfulAuthentication(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain, authResult: Authentication?) {
logger.info("Successful Authentication for user ${authResult?.principal}")
SecurityContextHolder.getContext().authentication = authResult
chain.doFilter(request, response)
}
Note: the example is in kotlin
Upvotes: 1