songhee24
songhee24

Reputation: 141

spring boot security custom successHandler with rest not working

not sure if my question is good..

Perhaps I was looking very badly for information about the spring security In general, I hope it will not be difficult for you to answer.


The question is, I use spring security with my login page. The login page is just in the public templates folder. I do not create a separate Controller for it that would return the view page (would it be correct to create a controller for it that would return the view login page?). In any case, my code works even without this page view controller. But only my custom SuccessHandler does not work (which, after login, checks by roles and would redirect to another page). Should I redirect by role to the appropriate pages using a different approach? (I mean if ADMIN_ROLE after login is redirected to the admin-panel.html)

my security

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserServiceImpl userServiceImpl;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                .authorizeRequests()
                .antMatchers("/", "/templates/sign-up.html").permitAll()
                .antMatchers("/api/users", "/api/users/login").permitAll()
                .antMatchers("/templates/admin-panel.html").hasRole("ADMIN")
                .antMatchers("/all-users").hasRole("ADMIN")
                .antMatchers("/news").hasRole("USER")
        .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/templates/login.html")
                .defaultSuccessUrl("/")
                .permitAll()
                .successHandler(myAuthenticationSuccessHandler())
                .and()
                .logout()
                .permitAll()
                .logoutSuccessUrl("/index.html");

        http.csrf().disable();
    }
    @Override
    public void configure(WebSecurity web) {
        web
                .ignoring()
                .antMatchers("/css/**")
                .antMatchers("/js/**")
                .antMatchers("/static/**")
                .antMatchers("/resources/**");
    }

    @Autowired
    protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userServiceImpl).passwordEncoder(bCryptPasswordEncoder());
    }

    @Bean
    public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
        return new CustomAuthenticationSuccessHandler();
    }
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

my custom success handler

public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    protected final Log logger = LogFactory.getLog(this.getClass());

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public CustomAuthenticationSuccessHandler() {
        super();
    }

    // API

    @Override
    public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
        handle(request, response, authentication);
        clearAuthenticationAttributes(request);
    }

    // IMPL

    protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
        final String targetUrl = determineTargetUrl(authentication);

        if (response.isCommitted()) {
            logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
            return;
        }

        redirectStrategy.sendRedirect(request, response, targetUrl);
    }

    protected String determineTargetUrl(final Authentication authentication) {

        Map<String, String> roleTargetUrlMap = new HashMap<>();
        roleTargetUrlMap.put("ROLE_USER", "/index.html");
        roleTargetUrlMap.put("ROLE_ADMIN", "/templates/admin-panel.html");

        final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (final GrantedAuthority grantedAuthority : authorities) {

            String authorityName = grantedAuthority.getAuthority();
            if(roleTargetUrlMap.containsKey(authorityName)) {
                return roleTargetUrlMap.get(authorityName);
            }
        }

        throw new IllegalStateException();
    }

    /**
     * Removes temporary authentication-related data which may have been stored in the session
     * during the authentication process.
     */
    protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
        final HttpSession session = request.getSession(false);

        if (session == null) {
            return;
        }

        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    }
}

my controller

    @CrossOrigin
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
    
        private final UserServiceImpl userService;
        private AuthenticationManager authenticationManager;
        public UserController(UserServiceImpl userService, AuthenticationManager authenticationManager) {
            this.userService = userService;
            this.authenticationManager = authenticationManager;
        }
    
        @PostMapping
        public ResponseEntity<?> register(@RequestBody UserDTO user) {
          try {
              userService.register(user);
              return new ResponseEntity<>("User added", HttpStatus.OK);
          } catch (Exception e) {
              return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
          }
        }
    
        @PostMapping(value = "/login")
        public ResponseEntity<?> login(@RequestBody UserDTO user, HttpServletResponse response) {
            try {
                Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
                boolean isAuthenticated = isAuthenticated(authentication);
                if (isAuthenticated) {
                    SecurityContextHolder.getContext().setAuthentication(authentication);
    //                response.sendRedirect("/templates/admin-panel.html");
   //                my pathetic attempt to create a redirect to another page
                }
                return new ResponseEntity<>("user authenticated", HttpStatus.OK);
            } catch (Exception e) {
                return new ResponseEntity<>(e, HttpStatus.FORBIDDEN);
            }
        }
        private boolean isAuthenticated(Authentication authentication) {
            return authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated();
        }

my static files enter image description here

Upvotes: 0

Views: 4250

Answers (1)

Johannes Becker
Johannes Becker

Reputation: 490

My Guess, as you didn't post your login page itself:

You don't need a controller listening to POST /login this normally automatically registered by Spring Security with all security related authentication stuff. No need to try it by yourself as in UserController.login(). I guess by regsitering this endpoint you override / deactivate the regular spring security behaviour.

Normally you just need a login page with a form that posts correctly to /login. The handling on the backend side is done by spring security itself.

See https://spring.io/guides/gs/securing-web/ for a minimal worling setup.

Upvotes: 1

Related Questions