通过前面两篇文章,我们已经知道了如何使用简单的配置来保护我们的应用了,根本不需要去管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));
}
}
}