Thymeleaf + Spring Security 集成基础
你是否已经切换到 Thymeleaf,但登录页和错误页仍在使用 JSP?在本文中,我们将看到如何配置你的 Spring 应用程序以使用 Thymeleaf 来处理登录页和错误页。
此处展示的所有代码都来自一个可运行的应用程序。你可以查看或下载源代码其 GitHub 仓库.
注意注意,自 Spring Security 5 起,Thymeleaf 为 Spring Security 提供的集成包支持 Spring MVC 和 Spring WebFlux 应用程序,但本文将专注于 Spring MVC 的配置。
前提条件
我们假设你已经熟悉 Thymeleaf 和 Spring Security,并且你拥有一个使用这些技术的正在运行的应用程序。如果你不了解 Spring Security,你可能有兴趣阅读Spring Security 官方文档.
登录页面
使用 Spring Security,你可以指定任意 URL 作为登录页面,就像这样:
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login.html")
.failureUrl("/login-error.html")
.and()
.logout()
.logoutSuccessUrl("/index.html");
}
现在我们必须在 Spring 控制器中匹配这些页面:
@Controller
public class MainController {
...
// Login form
@RequestMapping("/login.html")
public String login() {
return "login.html";
}
// Login form with error
@RequestMapping("/login-error.html")
public String loginError(Model model) {
model.addAttribute("loginError", true);
return "login.html";
}
}
请注意,我们为两个页面使用了同一个模板login.html但是当出现错误时,我们在模型中设置了一个布尔属性。
我们的login.html模板如下所示:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login page</title>
</head>
<body>
<h1>Login page</h1>
<p th:if="${loginError}" class="error">Wrong user or password</p>
<form th:action="@{/login.html}" method="post">
<label for="username">Username</label>:
<input type="text" id="username" name="username" autofocus="autofocus" /> <br />
<label for="password">Password</label>:
<input type="password" id="password" name="password" /> <br />
<input type="submit" value="Log in" />
</form>
</body>
</html>
错误页面
我们还可以配置基于 Thymeleaf 的错误页面。在这种情况下,Spring Security 并不参与其中,我们只需要像下面这样在 Spring 配置中添加一个ExceptionHandler即可:
@ControllerAdvice
public class ErrorController {
private static Logger logger = LoggerFactory.getLogger(ErrorController.class);
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String exception(final Throwable throwable, final Model model) {
logger.error("Exception during execution of SpringSecurity application", throwable);
String errorMessage = (throwable != null ? throwable.getMessage() : "Unknown error");
model.addAttribute("errorMessage", errorMessage);
return "error";
}
}
这个error.html模板内容可能如下所示:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Error page</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="css/main.css" th:href="@{/css/main.css}" />
</head>
<body th:with="httpStatus=${T(org.springframework.http.HttpStatus).valueOf(#response.status)}">
<h1 th:text="|${httpStatus} - ${httpStatus.reasonPhrase}|">404</h1>
<p th:utext="${errorMessage}">Error java.lang.NullPointerException</p>
<a href="index.html" th:href="@{/index.html}">Back to Home Page</a>
</body>
</html>
注意我们如何使用 Spring 的HttpStatus
枚举来获取关于已设置响应状态的详细信息(在本例中始终是500
,但这允许我们在其他错误报告场景中使用此error.html
)。
Spring Security 方言
在 Spring MVC 环境中,Spring Security 集成模块作为Spring Security 标签库.
的替代品。在这个示例中,我们使用该方言来显示登录用户的凭据并为不同角色显示不同的内容。
这个sec:authorize属性在其表达式求值为true:
<div sec:authorize="isAuthenticated()">
This content is only shown to authenticated users.
</div>
<div sec:authorize="hasRole('ROLE_ADMIN')">
This content is only shown to administrators.
</div>
<div sec:authorize="hasRole('ROLE_USER')">
This content is only shown to users.
</div>
这个时渲染其内容。属性用于打印登录用户名和角色:
Logged user: <span sec:authentication="name">Bob</span>
Roles: <span sec:authentication="principal.authorities">[ROLE_USER, ROLE_ADMIN]</span>