文章目录

  • 1. SecurityConfig 配置登录表单
  • 2. 登录成功后的处理:AuthenticationSuccessHandler
  • 3. 登录失败后的处理:AuthenticationFailureHandler
  • 4. 注销登录
  • 5. 注销登录成功后的处理:LogoutSuccessHandler


1. SecurityConfig 配置登录表单

定义一个登录页面:login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h2>自定义登录页面</h2>
<form action="/user/login" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2"><button type="submit">登录</button></td>
        </tr>
    </table>
</form>
</body>
</html>

定义2个测试接口,作为受保护的资源,当用户登录成功后,就可以访问受保护的资源 :

@RestController
public class UserResource {

    @RequestMapping("/hello")
    public String test(){
        return "hello";
    }

    @RequestMapping("/index")
    public String test1(){
        return "index";
    }
}

提供 SpringSecurity 的配置类 :

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问 
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
            	// 开启表单登录配置
            	// loginProcessingUrl ,usernameParameter ,passwordParameter 
            	// 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
            		// 配置登录页面地址
                    .loginPage("/login.html")
            		// 配置登录接口地址
                    .loginProcessingUrl("/user/login")
            		// 配置登录成功的跳转地址
                    .defaultSuccessUrl("/index")
            		// 配置登录失败的跳转地址
                    .failureUrl("/login.html")
            		// 登录用户名的参数名称
                    .usernameParameter("username")
            		// 登录密码的参数名称
                    .passwordParameter("password")
            		// 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
            	// 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
            	// 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .csrf().disable();
    }
}

配置完成后,启动 SpringBoot 项目,浏览器地址栏中输入 http://localhost:8080/index,会自动跳转到http://localhost:8080/login.html页面:

springsecurity注销 springsecurity登录注册_前端

输入用户名和密码进行登录,登录成功后,会自动跳转到登录前访问的 http://localhost:8080/index 页面,经过上面的配置,已经成功的自定义了一个登录页面出来,用户在登录成功之后,就可以访问受保护的资源了。

2. 登录成功后的处理:AuthenticationSuccessHandler

defaultSuccessUrl 表示用户登录成功之后,会自动重定向到登录之前的地址上,如果用户本身就是直接访问的登录页面,则登录成功后就会重定向到defaultSuccessUrl 指定的页面中。例如,用户在未认证的情况下,访问了/hello 页面;而用户如果一开始就访问登录页面,则登录成功后会自动重定向到defaultSuccessUrl 所指定的页面中。

第一种情况:用户登录成功之后,会自动重定向到登录之前的地址上

浏览器地址栏中输入 http://localhost:8080/hello,会自动跳转到 http://localhost:8080/login.html 页面:

springsecurity注销 springsecurity登录注册_数据库_02


输入用户名和密码 登录成功后,会自动跳转到登录之前访问的 http://localhost:8080/hello,而不是defaultSuccessUrl 所指定的 http://localhost:8080/index 页面。

第二种情况:一开始就访问登录页面,则登录成功后会自动重定向到defaultSuccessUrl 所指定的页面中

浏览器直接访问 http://localhost:8080/login.html 页面 :

springsecurity注销 springsecurity登录注册_前端_03


输入用户名和密码登录成功后,自动跳转到了defaultSuccessUrl 所指定的 http://localhost:8080/index 页面;

那么如果想登录前不访问登录页面,而登录后又想跳转到指定的页面呢 ?

当用户登录成功后,除了defaultSuccessUrl() 方法可试下登录成功后的跳转之外,successForwardUrl() 方法也可以实现登录成功后的跳转:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/user/login")
                    .successForwardUrl("/index")
                    .failureUrl("/login.html")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }
}

successForwardUrl 不会来了用户之前的访问地址,只要用户登录陈宫,就会通过服务器跳转到 successForwardUrl 所指定的页面。

defaultSuccessUrl 有一个重载的方法,如果重载方法的第2个参数传true ,则defaultSuccessUrl 的效果与successForwardUrl 类似,即不用考虑用户之前的访问地址,只要登录成功,就重定向到defaultSuccessUrl 所指定的页面。不同之处在于defaultSuccessUrl 是通过重定向实现的跳转,而 successForwardUrl 是通过服务器端跳转实现的。

无论是 defaultSuccessUrl 还是 successForwardUrl 最终配置的都是 AuthenticationSuccessHandler 接口的实例。SpringSecurity 中专门提供了AuthenticationSuccessHandler 接口用来处理登录成功的事项:

public interface AuthenticationSuccessHandler {
   
    default void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        this.onAuthenticationSuccess(request, response, authentication);
        chain.doFilter(request, response);
    }

    // 处理登录成功的具体事项,其中authentication参数保存了登录成功的用户信息
    void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}

AuthenticationSuccessHandler 接口有三个实现类:

(1) SimpleUrlAuthenticationSuccessHandler 通过handler() 方法实现请求的重定向;

(2) ForwardAuthenticationSuccessHandler 实现服务端的跳转;

(3) SavedRequestAwareAuthenticationSuccessHandler 在 SimpleUrlAuthenticationSuccessHandler 的基础上增加了请求缓存的功能,可以记录之前请求的地址,进而登录成功后重定向到一开始访问的地址;

SavedRequestAwareAuthenticationSuccessHandler 在 SimpleUrlAuthenticationSuccessHandler 的基础上增加了请求缓存的功能,可以记录之前请求的地址,进而登录成功后重定向到一开始访问的地址;

AuthenticationSuccessHandler 默认的三个实现类,无论哪一个都是用来出来页面跳转的。但是在前后端分离的开发中,用户登录成功后,不再需要页面跳转了,只需要给前端返回一个 JSON 数据即可,告诉前端登录成功还是失败,前端收到消息后自行处理,像这样的请求我们可以通过自定义AuthenticationSuccessHandler 的实现类来完成:

public class MyAthenticationSuccessHandler implements AuthenticationSuccessHandler {
    // 重写onAuthenticationSuccess()方法
    @Override
    public void onAuthenticationSuccess(
            HttpServletRequest request,
            HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        
        response.setContentType("application/json;charset=utf-8");
        Map<String, String> respMap = Map.of(
                "code", "200",
                "message", "登录成功"
        );
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(respMap);
        // 返回一段登录成功的Json字符串给前端
        response.getWriter().write(json);
    }
}

在 SecurityConfig 中配置自定义的 MyAthenticationSuccessHandler

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(new MyAthenticationSuccessHandler())
                    .failureUrl("/login.html")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }
}

重启项目,访问: http://localhost:8080/index

springsecurity注销 springsecurity登录注册_java_04

输入用户名和 密码,登录成功后,返回一段字符串:

springsecurity注销 springsecurity登录注册_html_05


上面 MyAthenticationSuccessHandler 可以简写在 SecurityConfig 类中:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(jsonAthenticationSuccessHandler())
                    .failureHandler("/login.html"
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}
3. 登录失败后的处理:AuthenticationFailureHandler

为了在前端页面展示登录失败的异常信息,首先在项目的pom.xml文件中引入thymeleaf依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

然后再resources/template目录下创建 mylogin.html 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h2>自定义登录页面</h2>
<form action="/user/login" method="post">
    <div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2"><button type="submit">登录</button></td>
        </tr>
    </table>
</form>
</body>
</html>

mylogin.html 和前面的 login.html 基本类似,前面的login.html是静态页面,这里是模板页面,mylogin.html 页面中加了一个div,用来展示登录失败的异常信息,登录失败的异常信息会放在request中返回给前端。

动态页面不能直接访问,需要一个访问控制器:

@Controller
public class LoginController {
    @RequestMapping("/mylogin.html")
    public String login(){
        return "mylogin";
    }
}

在SecurityConfig中配置登录页面和登录失败时的跳转页面:

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(new MyAthenticationSuccessHandler())
                    .failureUrl("/mylogin.html")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }
}

failureUrl 表示登录失败后重定向到 login.html 页面。重定向是一种客户端跳转,重定向不方便携带请求失败的异常信息(只能放在URL中)。如果希望能够在前端页面展示请求失败的异常信息,可以使用下面这种方式:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(new MyAthenticationSuccessHandler())
                    .failureForwardUrl("/login.html")
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }
}

failureForwardUrl 从名字上就可以看出,这种跳转是一种服务器跳转,服务器跳转的好处是可以携带登录异常信息。如果登录失败,自动跳转到登录页面后,就可以将错误信息展示出来,如图:输入错误的用户名或密码

springsecurity注销 springsecurity登录注册_数据库_06


无论是 failureUrl 还是 failureForwardUrl ,最终所配置的都是 AuthenticationFailureHandler 接口的实现:

public interface AuthenticationFailureHandler {
    void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException;
}

AuthenticationFailureHandler 接口只有一个方法,用来处理登录失败的请求,exception表示登录失败的异常信息。

AuthenticationFailureHandler 接口共有5个实现类:

(1) SimpleUrlAuthenticationFailureHandler 默认的处理逻辑就是通过充电功能项跳转到登录页面,当然也可以通过配置 forwardToDestination 属性将重定向改为服务器跳转,failureUrl 方法的底层实现逻辑就是SimpleUrlAuthenticationFailureHandler ;

(2) ExceptionMappingAuthenticationFailureHandler 可以实现根据不同的异常类型,映射不同的路径;

(3) ForwardAuthenticationFailureHandler 表示通过服务器端跳转来重新回到登录页面,failureForwardUrl 方法的底层实现逻辑就是 ForwardAuthenticationFailureHandler ;

(4) AuthenticationEntryPointFailureHandler 可以通过AuthenticationEntryPoint来处理登录异常;

(5) DelegatingAuthenticationFailureHandler 可以实现为不同的异常类型配置不同的登录失败处理回调;

看一下 SimpleUrlAuthenticationFailureHandler 类的源码:

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() {
    }

    // 构造对象时传入登录失败时要跳转的地址 defaultFailureUrl
    public SimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) {
        this.setDefaultFailureUrl(defaultFailureUrl);
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // 如果登录失败时要跳转的地址defaultFailureUrl==null,直接通过response返回异常信息
        if (this.defaultFailureUrl == null) {
            this.logger.debug("No failure URL set, sending 401 Unauthorized error");
            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
        } else {
            // 调用 saveException() 方法
            this.saveException(request, exception);
            // 如果 forwardToDestination 为true,就通过服务器端跳转到登录页面
            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) {
        // 如果forwardToDestination 属性为true,表示通过服务器端跳转回到登录页面
        // 把异常信息放在request中
        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);
            }
        }
    }
    
    public void setUseForward(boolean forwardToDestination) {
        this.forwardToDestination = forwardToDestination;
    }
    // ....
}

因此,假如不使用 failureForwardUrl 方法,同时又想在登录失败后通过服务端跳转到登录页面,那么可以自定义 SimpleUrlAuthenticationFailureHandler 配置,并将 forwardToDestination 属性置为 true:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(jsonAthenticationSuccessHandler())
                    .failureHandler(failureHandler())
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private SimpleUrlAuthenticationFailureHandler failureHandler() {
        SimpleUrlAuthenticationFailureHandler handler
                = new SimpleUrlAuthenticationFailureHandler("/mylogin.html");
        // 将 forwardToDestination 属性置为 true
        handler.setUseForward(true);
        return handler;
    }
}

这样配置之后,如果用户再次登录失败,就会通过服务端跳转重新回到登录页面,登录页面也会展示相应的错误信息,效果和 failureForwardUrl 一样。

如果是前后端分离开发,登录失败就不需要页面跳转了,只需要返回Json字符串给前端即可,此时可以通过自定义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, String> respMap = Map.of(
                "code", "500",
                "message", "登录失败" + exception.getMessage()
        );
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(respMap);
        response.getWriter().write(json);
    }
}

在 SecurityConfig 中进行配置:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(new MyAthenticationSuccessHandler())
                    .failureHandler(new MyAuthenticationFailureHandler())
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }
}

配置完成后,当用户再次登录失败,就不会进行页面跳转了,而是直接返回 json 字符串:

springsecurity注销 springsecurity登录注册_前端_07


上面的 MyAuthenticationFailureHandler 简写在SecurityConfig中:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/user/login")
                    .successHandler(jsonAthenticationSuccessHandler())
                    .failureHandler(jsonAuthenticationFailureHandler())
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .permitAll()
                .and()
                .csrf().disable();
    }

    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}
4. 注销登录

SpringSecurity中提供了默认的注销登录页面,我们也可以根据自己的需求对注销登录进行定制。

在 SecurityConfig 中配置注销登录:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问 
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
                // 开启表单登录配置
                // loginProcessingUrl ,usernameParameter ,passwordParameter 
                // 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
                    // 配置登录页面地址
                    .loginPage("/mylogin.html")
                    // 配置登录接口地址
                    .loginProcessingUrl("/user/login")
                    // 配置登录成功的跳转地址
                    .successHandler(jsonAthenticationSuccessHandler())
                    // 配置登录失败的跳转地址
                    .failureHandler(jsonAuthenticationFailureHandler())
                    // 登录用户名的参数名称
                    .usernameParameter("username")
                    // 登录密码的参数名称
                    .passwordParameter("password")
                    // 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                    // 指定注销登录请求地址,默认是GET请求,路径为/logout
                    .logoutUrl("/logout")
                    // 表示是否使session失效,默认为true
                    .invalidateHttpSession(true)    
                    // 表示是否清除认证信息,默认为true
                    .clearAuthentication(true)
                    // 表示注销登录后的跳转地址
                    .logoutSuccessUrl("/mylogin.html")
                .and()
                // 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
            	// 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .csrf().disable();
    }

    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}

启动项目访问 http://localhost:8080/logout,自动跳转到登录页面:

springsecurity注销 springsecurity登录注册_数据库_08


登录成功后在浏览器输入 http://localhost:8080/logout 就可以发起注销登录请求了,注销登录成功后,会自动跳转到 mylogin.html 页面。

如果项目有需要还可以配置多个注销登录的请求,同时还可以指定请求的方法:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
                // 开启表单登录配置
                // loginProcessingUrl ,usernameParameter ,passwordParameter
                // 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
                    // 配置登录页面地址
                    .loginPage("/mylogin.html")
                    // 配置登录接口地址
                    .loginProcessingUrl("/user/login")
                    // 配置登录成功的跳转地址
                    .successHandler(jsonAthenticationSuccessHandler())
                    // 配置登录失败的跳转地址
                    .failureHandler(jsonAuthenticationFailureHandler())
                    // 登录用户名的参数名称
                    .usernameParameter("username")
                    // 登录密码的参数名称
                    .passwordParameter("password")
                    // 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                    // 指定注销登录请求地址和请求方式
                    // 配置两个注销登录的请求,使用任意一个请求都可以完成登录注销
                    .logoutRequestMatcher(new OrRequestMatcher(
                            new AntPathRequestMatcher("/logout","GET"),
                            new AntPathRequestMatcher("/logout2","POST")
                            )
                    )
                    // 表示是否使session失效,默认为true
                    .invalidateHttpSession(true)
                    // 表示是否清除认证信息,默认为true
                    .clearAuthentication(true)
                    // 表示注销登录后的跳转地址
                    .logoutSuccessUrl("/mylogin.html")
                // 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
                // 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .and()
                .csrf().disable();
    }

    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}
5. 注销登录成功后的处理:LogoutSuccessHandler

如果项目是前后端分离的项目,注销成功后就不需要页面跳转了,只需要将注销成功的信息返回给前端即可,此时我们可以自定义返回内容,实现成功退出登录的接口LogoutSuccessHandler,自定义一个注销登录类

public interface LogoutSuccessHandler {
    void onLogouauthenticationtSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException;
}
public class MyLogoutSuccessfulHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        Map<String, String> respMap = Map.of(
                "code", "200",
                "message", "注销登录成功"
        );
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(respMap);
        response.getWriter().write(json);
    }
}

在 SecurityConfig 中配置:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
                // 开启表单登录配置
                // loginProcessingUrl ,usernameParameter ,passwordParameter
                // 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
                    // 配置登录页面地址
                    .loginPage("/mylogin.html")
                    // 配置登录接口地址
                    .loginProcessingUrl("/user/login")
                    // 配置登录成功的跳转地址
                    .successHandler(jsonAthenticationSuccessHandler())
                    // 配置登录失败的跳转地址
                    .failureHandler(jsonAuthenticationFailureHandler())
                    // 登录用户名的参数名称
                    .usernameParameter("username")
                    // 登录密码的参数名称
                    .passwordParameter("password")
                    // 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                    // 指定注销登录请求地址和请求方式
                    .logoutSuccessHandler(new MyLogoutSuccessfulHandler())
                    // 表示是否使session失效,默认为true
                    .invalidateHttpSession(true)
                    // 表示是否清除认证信息,默认为true
                    .clearAuthentication(true)
                    // 表示注销登录后的跳转地址
                    .logoutSuccessUrl("/mylogin.html")
                // 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
                // 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .and()
                .csrf().disable();
    }

    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}

启动项目,登录成功后,注销登录访问 localhost:8080/logout,会将注销登录的成功信息返回给前端:

springsecurity注销 springsecurity登录注册_前端_09


将上面的配置方式简写为:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
                // 开启表单登录配置
                // loginProcessingUrl ,usernameParameter ,passwordParameter
                // 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
                    // 配置登录页面地址
                    .loginPage("/mylogin.html")
                    // 配置登录接口地址
                    .loginProcessingUrl("/user/login")
                    // 配置登录成功的跳转地址
                    .successHandler(jsonAthenticationSuccessHandler())
                    // 配置登录失败的跳转地址
                    .failureHandler(jsonAuthenticationFailureHandler())
                    // 登录用户名的参数名称
                    .usernameParameter("username")
                    // 登录密码的参数名称
                    .passwordParameter("password")
                    // 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                    // 指定注销登录请求地址和请求方式
                    // 配置两个注销登录的请求,使用任意一个请求都可以完成登录注销
                    .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout","GET"),
                        new AntPathRequestMatcher("/logout2","POST")
                        )
                    )
                    // 表示是否使session失效,默认为true
                    .invalidateHttpSession(true)
                    // 表示是否清除认证信息,默认为true
                    .clearAuthentication(true)
                    // 不再页面跳转,将注销成功的信息返回给前端
                    .logoutSuccessHandler(jsonLogoutSuccessfulHandler())
                // 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
                // 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .and()
                .csrf().disable();
    }

    private LogoutSuccessHandler jsonLogoutSuccessfulHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "注销登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}

如果希望为不同的注销地址返回不同的结果,也是可以的,配置如下:

@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // authorizeRequests() 方法表示开启权限配置
        http.authorizeRequests()
                // 表示所有的请求都要认证后才能访问
                .anyRequest().authenticated()
                // and()方法相当于又回到 HttpSecurity 实例,重新开启新一轮的配置。
                .and()
                // 开启表单登录配置
                // loginProcessingUrl ,usernameParameter ,passwordParameter
                // 和login.html中登录表单的配置一致,即action,用户名name属性值,密码name属性值;
                .formLogin()
                    // 配置登录页面地址
                    .loginPage("/mylogin.html")
                    // 配置登录接口地址
                    .loginProcessingUrl("/user/login")
                    // 配置登录成功的跳转地址
                    .successHandler(jsonAthenticationSuccessHandler())
                    // 配置登录失败的跳转地址
                    .failureHandler(jsonAuthenticationFailureHandler())
                    // 登录用户名的参数名称
                    .usernameParameter("username")
                    // 登录密码的参数名称
                    .passwordParameter("password")
                    // 跟登录相关的页面和接口不做拦截,直接通过
                    .permitAll()
                .and()
                // 开启注销登录配置
                .logout()
                    // 指定注销登录请求地址和请求方式
                    // 配置两个注销登录的请求,使用任意一个请求都可以完成登录注销
                    .logoutRequestMatcher(new OrRequestMatcher(
                        new AntPathRequestMatcher("/logout","GET"),
                        new AntPathRequestMatcher("/logout2","POST")
                        )
                    )
                    // 表示是否使session失效,默认为true
                    .invalidateHttpSession(true)
                    // 表示是否清除认证信息,默认为true
                    .clearAuthentication(true)
                    // 不再页面跳转,将注销成功的信息返回给前端
                    .defaultLogoutSuccessHandlerFor(
                            (request, response, authentication) -> {
                                response.setContentType("application/json;charset=utf-8");
                                Map<String, String> respMap = Map.of(
                                        "code", "200",
                                        "message", "logout 注销登录成功"
                                );
                                ObjectMapper objectMapper = new ObjectMapper();
                                String json = objectMapper.writeValueAsString(respMap);
                                response.getWriter().write(json);
                            },new AntPathRequestMatcher("/logout","GET")
                    )
                    .defaultLogoutSuccessHandlerFor(
                            (request, response, authentication) -> {
                                response.setContentType("application/json;charset=utf-8");
                                Map<String, String> respMap = Map.of(
                                        "code", "200",
                                        "message", "logout2 注销登录成功"
                                );
                                ObjectMapper objectMapper = new ObjectMapper();
                                String json = objectMapper.writeValueAsString(respMap);
                                response.getWriter().write(json);
                            },new AntPathRequestMatcher("/logout2","POST")
                    )
                // 禁用CSRF防御功能功能,SpringSecurity 自带了 CSRF 防御机制,
                // 但是我们这里为了测试方便,先将CSRF防御机制关闭
                .and()
                .csrf().disable();
    }
    
    private AuthenticationFailureHandler jsonAuthenticationFailureHandler() {
        return (request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "500",
                    "message", "登录失败" + exception.getMessage()
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }

    private AuthenticationSuccessHandler jsonAthenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            Map<String, String> respMap = Map.of(
                    "code", "200",
                    "message", "登录成功"
            );
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(respMap);
            response.getWriter().write(json);
        };
    }
}

defaultLogoutSuccessHandlerFor 方法可以注册过个不同的注销成功回调函数,该方法第一个参数时注销成功回调,第二个承诺书是具体的注销请求。当用户注销成功后,使用了哪个注销请求,就给出对应的响应信息。