Spring Boot Security 自定义登录和注销

  • by

Spring Security API 有高度的灵活性,允许程序员自定义身份验证和授权过程的各种细节。本文将介绍如何自定义 Spring Security API 的登录和注销详细信息。

自定义登录信息

使用表单的登录方法时,Spring Security 将显示默认的登录页面:

如果要使用自己的登录页面,可以用以下代码指定登录页面的 URL:

@Override
protected void configure(HttpSecurity http) throws Exception {
 
    http.formLogin().loginPage("/login");
    ...
}

为此,必须配置 Spring MVC 使视图名称映射 /login:

package org.91tech;
 
import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.*;
 
@Configuration
public class MvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
     
        registry.addViewController("/login").setViewName("login");
         
    }
 
}

然后编写登录页面的代码:

<!DOCTYPE html>
<html xmlns:th="http:/www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Login - Company ABC</title>
</head>
<body>
<div>
<form th:action="@{/login}" method="post" style="max-width: 400px; margin: 0 auto;">
    <p>
        E-mail: <input type="email" name="username" required />  
    </p>
    <p>
        Password: <input type="password" name="password" required />
    </p>
    <p>
        <input type="submit" value="Login" />
    </p>
</form>
</div>
</body>
</html>

默认情况下,Spring Security 使用 username 和 password 作为字段名,并且表单的 action 是 /login。 如果要使用其他字段名和 URL,可以用以下 Java 代码指定:

http.formLogin()
    .loginPage("/login")
    .usernameParameter("email")
    .passwordParameter("pass")
    .loginProcessingUrl("/doLogin");

并相应地更新登录表单的代码:

<form th:action="@{/doLogin}" method="post">
    <p>
        E-mail: <input type="email" name="email" required /> 
    </p>
    <p>
        Password: <input type="password" name="pass" required />
    </p>
    <p>
        <input type="submit" value="Login" />
    </p>
</form>

登录成功默认 URL

Spring Security 会将用户重定向到他在登录之前访问的页面。例如,如果用户先访问“创建新产品”页面,这时用户将被重定向到登录页面。 登录成功后,返回到创建新产品页面,这是正确的逻辑。

如果要在用户登录后向其显示单独的页面,可以指定默认的登录成功 URL:

http.formLogin()
    .defaultSuccessUrl("/login_success");

登录失败 URL

默认情况下,如果用户登录失败,Spring Security 将重定向到 /login?error。如果想更改此行为,例如,显示自己的页面,向用户显示登录错误消息,那么可以用以下代码设置登录失败 URL:

http.formLogin()
    .failureUrl("/login_error");

登录成功转发 URL

如果要在用户成功登录后执行一些额外的代码,例如,日志或审核,那么可以指定成功后转发的 URL:

http.formLogin()
    .successForwardUrl("/login_success_handler");

为此,必须在控制器类中编写相应的处理方法:

@Controller
public class AppController {
 
    @PostMapping("/login_success_handler")
    public String loginSuccessHandler() {
        System.out.println("Logging user login success...");
 
        return "index";
    }  
}

在此可以指定用户登录成功后将去向何处。

登录失败转发 URL

如果要在用户登录失败的情况下运行一些额外的代码,可以指定失败转发 URL:

http.formLogin()
    .failureForwardUrl("/login_failure_handler");

并在控制器中编写处理程序方法:

@PostMapping("/login_failure_handler")
public String loginFailureHandler() {
    System.out.println("Login failure handler....");
     
    return "login";
}

登录成功处理程序

可以编写身份验证成功处理程序代替登录成功转发 URL:

http.formLogin()
.successHandler(new AuthenticationSuccessHandler() {
     
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
         
        System.out.println("Logged user: " + authentication.getName());
         
        response.sendRedirect("/");
    }
});

由于可以直接访问 Authentication 对象,因此可以更好地控制行为。

登录失败处理程序

可以使用身份验证失败处理程序代替使用登录失败转发 URL:

http.formLogin()
.failureHandler(new AuthenticationFailureHandler() {
     
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        System.out.println("Login failed");
        System.out.println(exception);
         
        response.sendRedirect("/login_error");
    }
});

自定义注销信息

除了登录自定义之外,Spring Security 还允许程序员自定义注销过程。可以这样编写 Logout 按钮:

<form th:action="@{/logout}" method="post">
    <input type="submit" value="Logout" />
</form>

可以更改的第一件事就是注销 URL。

注销 URL

默认情况下,Spring Security 通过 HTTP POST 方法处理 /logout URL。可以配置 HttpSecurity 对象来更改此 URL:

http.logout()
    .logoutUrl("/doLogout");

为此,必须相应地更新注销表单的 action URL:

<form th:action="@{/doLogout}" method="post">

注销成功 URL

默认情况下,用户注销后将看到登录页面。 如果要向用户显示自定义页面,需要指定注销成功 URL:

http.logout()
    .logoutSuccessUrl("/logout_success");

注销成功处理程序

如果要在用户成功注销后执行其他步骤,可以使用注销成功处理程序:

http.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
     
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication)
            throws IOException, ServletException {
         
        System.out.println("This user logged out: " + authentication.getName());
         
        response.sendRedirect("/logout_success");
    }
});

使用注销链接代替按钮

之所以应该使用 Logout 按钮,是因为 Spring Security 在登录页面中会自动生成一个安全令牌来防止 CSRF(跨站点请求伪造)攻击。 例如:

<input type="hidden" name="_csrf" value="8ec12704-5ab6-4f0c-a758-2fc36f2c9368"/>

因此,必须通过 HTTP POST 方法发送注销请求。可以使用以下代码禁用 CSRF 保护,这样就可以使用注销链接(通过 HTTP GET 方法):

http.csrf().disable();

但是,这样的代码不建议用于实际生产环境。如果要用注销链接,那么最好的方法是隐藏注销表单,并使用 Javascript 来提交表单:

<form name="logoutForm" th:hidden="true" method="post" th:action="@{/doLogout}">
    <input type="submit" value="Logout" />
</form>
<a href="javascript: logoutForm.submit();">Sign Out</a>

⬅返回目录

发表评论

电子邮件地址不会被公开。 必填项已用*标注