通过前面两篇文章,我们已经知道了如何使用简单的配置来保护我们的应用了,根本不需要去管SpringSecurity的登录验证流程。如果我们需要自定义登录成功与失败处理,我们需要实现AuthenticationSuccessHandler接口和AuthenticationFailureHandler接口

1. 新建MyAuthenticationSuccessHandler类

@Component
@Slf4j
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    public static final String RETURN_TYPE = "html"; // 登录成功时,用来判断是返回json数据还是跳转html页面

    @Autowired
    private ObjectMapper objectMapper;

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登录成功");
        log.info("username=>" + request.getParameter("username"));
        if(RETURN_TYPE.equals("html")) {
            redirectStrategy.sendRedirect(request, response, "/user/index");
        } else {
            Map<String, Object> map = new HashMap<>();
            map.put("code","0");
            map.put("msg","登录成功");
            map.put("data",authentication);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(map));
        }
    }
}

2. 新建MyAuthenticationFailHandler类

@Component
@Slf4j
public class MyAuthenticationFailHandler implements AuthenticationFailureHandler {

    public static final String RETURN_TYPE = "html"; // 登录失败时,用来判断是返回json数据还是跳转html页面

    @Autowired
    private ObjectMapper objectMapper;

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败:" + exception.getMessage());
        log.info("username=>" + request.getParameter("username"));

        if (RETURN_TYPE.equals("html")) {
            redirectStrategy.sendRedirect(request, response, "/login/index?error=true");
        } else {
            Map<String, Object> map = new HashMap<>();
            map.put("code","1002");
            map.put("msg","登录失败");
            map.put("data",exception.getMessage());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(map));
        }
    }
}

3. 修改SpringSecurityConfig类

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserService myUserService;

    @Autowired
    private MyAuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private MyAuthenticationFailHandler authenticationFailHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/authentication/*","/login/*") // 不需要登录就可以访问
                .permitAll()
                .antMatchers("/user/**").hasAnyRole("USER") // 需要具有ROLE_USER角色才能访问
                .antMatchers("/admin/**").hasAnyRole("ADMIN") // 需要具有ROLE_ADMIN角色才能访问
                .anyRequest().authenticated()
                .and()
                    .formLogin()
                    .loginPage("/authentication/login") // 访问需要登录才能访问的页面,如果未登录,会跳转到该地址来
                    .loginProcessingUrl("/authentication/form")
                    .successHandler(authenticationSuccessHandler)
                    .failureHandler(authenticationFailHandler)
                ;
    }

    // 密码加密方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    // 重写方法,自定义用户
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserService); // 注入MyUserService,这样SpringSecurity会调用里面的loadUserByUsername(String s)
    }
}

4. LoginController

@Controller
public class LoginController {

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public static final String RETURN_TYPE = "html"; // 需要登录时,用来判断是返回json数据还是跳转html页面

    // 如果用户访问的界面需要登录则会跳转到该路径,在这里判断是返回json格式的数据还是返回html页面
    @GetMapping("/authentication/login")
    @ResponseBody
    public Object authenticationLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (RETURN_TYPE.equals("html")) {
            redirectStrategy.sendRedirect(request,response,"/login/index");
        }
        // 如果是需要返回json数据,则返回需要登录的信息提示
        Map<String, Object> map = new HashMap<>();
        map.put("code",1001);
        map.put("msg","需要登录");
        return map;
    }


    // 登录页面
    @GetMapping("/login/index")
    public String loginIndex() throws IOException {
        return "login";
    }
}

到这里已经配置完成了。

 

如果这样写的话,还存在一个问题,假设登录成功默认跳转到A页面。在没有登录的情况下访问B页面,此时会先跳转到登录页面,登录成功后,按照上面的逻辑应该是跳转到A页面,而我们是希望在登录成功之后直接跳转到B页面,那么该如何实现呢?

SpringSecurity已经有实现好的类直接供我们使用,修改后的代码如下:

@Component
@Slf4j
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    public static final String RETURN_TYPE = "html"; // 登录成功时,用来判断是返回json数据还是跳转html页面

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登录成功");
        log.info("username=>" + request.getParameter("username"));
        if(RETURN_TYPE.equals("html")) {
            super.setDefaultTargetUrl("/user/index"); // 设置默认登陆成功的跳转地址
            super.onAuthenticationSuccess(request, response, authentication);
        } else {
            Map<String, Object> map = new HashMap<>();
            map.put("code","0");
            map.put("msg","登录成功");
            map.put("data",authentication);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(map));
        }
    }
}

 

@Component
@Slf4j
public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {

    public static final String RETURN_TYPE = "html"; // 登录失败时,用来判断是返回json数据还是跳转html页面

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败:" + exception.getMessage());
        log.info("username=>" + request.getParameter("username"));

        if (RETURN_TYPE.equals("html")) {
            super.setDefaultFailureUrl("/login/index?error=true"); // 登录失败,跳转到登录界面
            super.onAuthenticationFailure(request, response, exception);
        } else {
            Map<String, Object> map = new HashMap<>();
            map.put("code","1002");
            map.put("msg","登录失败");
            map.put("data",exception.getMessage());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(map));
        }
    }
}