@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/mylogin.html") .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/index.html") .failureHandler(new MyAuthenticationFailureHandler()) .usernameParameter("uname") .passwordParameter("passwd") .permitAll() .and() .logout() .logoutRequestMatcher(new OrRequestMatcher( new AntPathRequestMatcher("/logout1", "GET"), new AntPathRequestMatcher("/logout2", "POST"))) .invalidateHttpSession(true) .clearAuthentication(true) .defaultLogoutSuccessHandlerFor((req,resp,auth)->{ resp.setContentType("application/json;charset=utf-8"); Map<String, Object> result = new HashMap<>(); result.put("status", 200); result.put("msg", "使用 logout1 注销成功!"); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(result); resp.getWriter().write(s); },new AntPathRequestMatcher("/logout1","GET")) .defaultLogoutSuccessHandlerFor((req,resp,auth)->{ resp.setContentType("application/json;charset=utf-8"); Map<String, Object> result = new HashMap<>(); result.put("status", 200); result.put("msg", "使用 logout2 注销成功!"); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(result); resp.getWriter().write(s); },new AntPathRequestMatcher("/logout2","POST")) .and() .csrf().disable(); } }
springSecurity需要自定义配置值 基本都是继承WebSecurityConfigurerAdapter
其中loginProcessingUrl usernameParameter passwordParameter要和登录表单的配置一致。
.loginPage("/mylogin.html") // .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/index.html") .failureHandler(new MyAuthenticationFailureHandler()) .usernameParameter("uname") .passwordParameter("passwd")
csrf().disable()表示禁用CSRF防御功能
用户登录成功后除了defaultSuccessUrl方法可以实现登录成功的跳转之外,successForwardUrl也可以实现登录成功后的跳转,
defaultSuccessUrl 和successForwardUrl区别:
defaultSuccessUrl是客户端跳转重定向,successForwardUrl是通过服务端实现的跳转。
他们的接口都AuthenticationSuccessHandler
AuthenticationSuccessHandler有三个实现类
SimpleUrlAuthenticationSuccessHandler 继承 AbstractAuthenticationTargetUrlRequestHandler 通过他的handle方法处理请求
SavedRequestAwareAuthenticationSuccessHandler 在SimpleUrlAuthenticationSuccessHandler基础上增加了请求加缓存的功能,可以记录之前请求的地址,今儿在登录成功后重定向到开始访问的地址。
ForwardAuthenticationSuccessHandler 是服务端的跳转
SavedRequestAwareAuthenticationSuccessHandler
defaultSuccessUrl 对应的是SavedRequestAwareAuthenticationSuccessHandler
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { SavedRequest savedRequest = this.requestCache.getRequest(request, response); if (savedRequest == null) { super.onAuthenticationSuccess(request, response, authentication); } else { String targetUrlParameter = this.getTargetUrlParameter(); if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) { this.clearAuthenticationAttributes(request); String targetUrl = savedRequest.getRedirectUrl(); this.logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl); this.getRedirectStrategy().sendRedirect(request, response, targetUrl); } else { this.requestCache.removeRequest(request, response); super.onAuthenticationSuccess(request, response, authentication); } } }
ForwardAuthenticationSuccessHandler
successForwardUrl对应ForwardAuthenticationSuccessHandler
public class ForwardAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private final String forwardUrl; public ForwardAuthenticationSuccessHandler(String forwardUrl) { Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> { return "'" + forwardUrl + "' is not a valid forward URL"; }); this.forwardUrl = forwardUrl; } public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { request.getRequestDispatcher(this.forwardUrl).forward(request, response); } }
主要调用getRequestDispatcher进行服务端请求转发
自定义AuthenticationSuccessHandler 实现类
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler{ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); Map<String, Object> resp = new HashMap<>(); resp.put("status", 200); resp.put("msg", "登录成功!"); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(resp); response.getWriter().write(s); } }
.successHandler(new MyAuthenticationSuccessHandler())
通过HttpServletResponse对象返回登录成功的json给前端
failureUrl表示登录失败后的重定向到配置的页面,重定向是客户端的跳转,不方便携带请求失败的异常信息。
failureForwardUrl是服务端的跳转,可以携带登录异常信息。登录失败,自动跳转回登录页面,将错误信息展示出来。
他们的配置的是AuthenticationFailureHandler接口的实现类
SimpleUrlAuthenticationFailureHandler
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.web.authentication; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; public class SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler { protected final Log logger = LogFactory.getLog(this.getClass()); private String defaultFailureUrl; private boolean forwardToDestination = false; private boolean allowSessionCreation = true; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); public SimpleUrlAuthenticationFailureHandler() { } public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) { this.setDefaultFailureUrl(defaultFailureUrl); } public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { if (this.defaultFailureUrl == null) { this.logger.debug("No failure URL set, sending 401 Unauthorized error"); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); } else { this.saveException(request, exception); if (this.forwardToDestination) { this.logger.debug("Forwarding to " + this.defaultFailureUrl); request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response); } else { this.logger.debug("Redirecting to " + this.defaultFailureUrl); this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl); } } } protected final void saveException(HttpServletRequest request, AuthenticationException exception) { if (this.forwardToDestination) { request.setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception); } else { HttpSession session = request.getSession(false); if (session != null || this.allowSessionCreation) { request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", exception); } } } }
当用户构造SimpleUrlAuthenticationFailureHandler对象时候传入defaultFailureUrl,也就是登录失败时要跳转的url。在onAuthenticationFailure方法中
自定义AuthenticationFailureHandler实现类
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); Map<String, Object> resp = new HashMap<>(); resp.put("status", 500); resp.put("msg", "登录失败!" + exception.getMessage()); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(resp); response.getWriter().write(s); } }
通过HttpServletResponse对象返回登录失败的json给前端
.logout() .logoutUrl("") .logoutRequestMatcher(new OrRequestMatcher( new AntPathRequestMatcher("/logout1", "GET"), new AntPathRequestMatcher("/logout2", "POST"))) .invalidateHttpSession(true) .clearAuthentication(true) .logoutSuccessUrl("")
.logout() .logoutRequestMatcher(new OrRequestMatcher( new AntPathRequestMatcher("/logout1", "GET"), new AntPathRequestMatcher("/logout2", "POST"))) .invalidateHttpSession(true) .clearAuthentication(true) .defaultLogoutSuccessHandlerFor((req,resp,auth)->{ resp.setContentType("application/json;charset=utf-8"); Map<String, Object> result = new HashMap<>(); result.put("status", 200); result.put("msg", "使用 logout1 注销成功!"); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(result); resp.getWriter().write(s); },new AntPathRequestMatcher("/logout1","GET")) .defaultLogoutSuccessHandlerFor((req,resp,auth)->{ resp.setContentType("application/json;charset=utf-8"); Map<String, Object> result = new HashMap<>(); result.put("status", 200); result.put("msg", "使用 logout2 注销成功!"); ObjectMapper om = new ObjectMapper(); String s = om.writeValueAsString(result); resp.getWriter().write(s); },new AntPathRequestMatcher("/logout2","POST")) .and() .csrf().disable();
defaultLogoutSuccessHandlerFor()两个参数 第一个是注销成功的回调,第二个是具体的注销请求。