目录
配合Thymeleaf的Spring Security用法解析
1. 引入Spring Security和Thymeleaf依赖
3. Thymeleaf与Spring Security标签
SpringBoot+Thymeleaf完成安全认证,登录登出操作。
Spring Security介绍
Spring Security是一个强大的认证和授权框架,用于保护基于Spring的应用程序。它提供了全面的安全功能,包括认证、授权、攻击防护等,能够有效地帮助开发者构建安全的Web应用程序。
Spring Security的核心功能:
1.认证(Authentication):
- 用于确认用户身份,常见的认证方式有用户名密码、OAuth、LDAP等。
- Spring Security支持多种身份验证机制,如基于表单的认证、HTTP基本认证等。
2.授权(Authorization):
- 用于确定用户是否有权限访问特定资源或操作。
- 可以基于角色、权限进行细粒度的控制,也可以根据访问路径、HTTP方法进行安全控制。
3.CSRF防护:
- Spring Security内置了对跨站请求伪造(CSRF)攻击的防护。
4.会话管理:
- 提供会话固定 Fixation)防护和并发会话控制。攻击(Session)
5.密码加密:
- 提供了多种密码加密方式,常用的如BCrypt、PBKDF2等。
6.自定义过滤器
- 可以在Spring Security的过滤链中插入自定义过滤器,满足业务需求。
配合Thymeleaf的Spring Security用法解析
Thymeleaf是一个流行的Java模板引擎,广泛用于Web应用程序中构建动态内容。与Spring Security结合使用时,Thymeleaf可以通过其springSecurity
命名空间来帮助处理与安全相关的元素。
1. 引入Spring Security和Thymeleaf依赖
在pom.xml
中添加以下依赖:
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Security Thymeleaf -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
</dependencies>
2. 配置Spring Security
在Spring Boot中,可以通过application.properties
或者application.yml
来配置Spring Security的基础功能。
# 禁用默认的登录页面
spring.security.form.login.enabled=false
# 自定义登录页面URL
spring.security.form.login.login-page=/login
3. Thymeleaf与Spring Security标签
在Thymeleaf模板中,可以使用Spring Security提供的标签来进行权限控制、条件判断等。
登录表单:通过<form>
和Spring Security的标签,可以创建一个登录表单。
<form th:action="@{/login}" method="post">
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username" required="required"/>
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password" required="required"/>
</div>
<div>
<button type="submit">Login</button>
</div>
</form>
授权控制:使用sec:authorize
标签来控制某些元素的可见性或执行的条件。
<div sec:authorize="isAuthenticated()">
<!-- 只有已认证用户才能看到的内容 -->
<p>Welcome, authenticated user!</p>
</div>
<div sec:authorize="hasRole('ADMIN')">
<!-- 只有ADMIN角色用户能看到的内容 -->
<p>Admin dashboard</p>
</div>
退出登录:通过Thymeleaf与Spring Security集成,可以创建一个退出登录的链接。
<a sec:authorize="isAuthenticated()" href="/logout">Logout</a>
4. Spring Security表单认证
Spring Security提供了一些基本的配置来启用基于表单的认证。可以在配置类中自定义登录页面、注销页面、登录失败和成功的处理等。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/css/**", "/js/**").permitAll() // 允许所有人访问登录页面和静态资源
.anyRequest().authenticated() // 其他页面需要认证
.and()
.formLogin()
.loginPage("/login") // 自定义登录页面
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置内存中的用户和角色
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5. 示例:登录页面与权限控制
创建一个简单的登录页面login.html
,通过Spring Security控制访问权限:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="#" th:action="@{/login}" method="post">
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username" />
</div>
<div>
<label for="password">Password:</label>
<input type="password" name="password" id="password" />
</div>
<button type="submit">Login</button>
</form>
<div sec:authorize="isAuthenticated()">
<p>You are logged in!</p>
</div>
</body>
</html>
SpringBoot+Thymeleaf完成安全认证,登录登出操作。
1.导入Maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.31</version>
</dependency>
2.AuthInterceptor拦截器
package com.book.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
// 排除登录、注册等公开页面
String uri = request.getRequestURI();
if (uri.startsWith("/user/login") || uri.startsWith("/user/register") ||
uri.startsWith("/static/") || uri.equals("/")) {
return true;
}
// 检查session中的用户信息
if (session == null || session.getAttribute("user") == null) {
response.sendRedirect("/user/login");
return false;
}
return true;
}
}
3.WebConfig注册拦截器
package com.book.config;
import com.book.interceptor.AuthInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {
/**
* 注册自定义拦截器
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(
"/user/login",
"/user/register",
"/css/**",
"/js/**",
"/error"
);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 映射本地文件系统到 "/uploads/**"
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:///F:/SpringBoot/libraryManagement/uploads/");
}
}
4.SecurityConfig安全配置
package com.book.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/login", "/user/register", "/css/**","/js/**").permitAll()
.anyRequest().authenticated()
.and()
// 完全禁用Spring Security的表单登录
.formLogin().disable()
// 禁用默认的/logout处理
.logout().disable()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.sessionManagement()
.sessionFixation().migrateSession()
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.expiredUrl("/user/login?expired=true");
}
}
5.Controller-登录登出操作
登录成功后重定向至book/list
@PostMapping("/login")
public String handleLogin(@RequestParam String username,
@RequestParam String password,
HttpSession session) {
log.info("登录请求 - username: {}", username);
User loginUser = userService.getUserByUsername(username);
if (loginUser == null || !HashUtils.verifyPassword(password, loginUser.getPassword(), loginUser.getSalt())) {
return "redirect:/user/login?error=true";
}
// 1. 设置session
session.setAttribute("user", loginUser);
// 2. 设置Spring Security认证上下文(关键)
Authentication auth = new UsernamePasswordAuthenticationToken(
loginUser.getUsername(),
null,
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) // 假设所有用户都有USER角色
);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/book/list";
}
//登出操作,使session事物失效
@GetMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/user/login";
}
6.login.html用户登录页面
ps:表单一定要包含这部分
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<div class="card shadow">
<div class="card-body">
<h3 class="card-title text-center mb-4">用户登录</h3>
<!-- 错误消息 -->
<div th:if="${errorMsg}" class="alert alert-danger" th:text="${errorMsg}"></div>
<!-- 传统表单提交到自定义Controller -->
<form th:action="@{/user/login}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">登录</button>
</div>
</form>
<div class="mt-3 text-center">
<a th:href="@{/user/register}" class="text-decoration-none">还没有账号?立即注册</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
总结
Spring Security和Thymeleaf结合使用,可以轻松地在Web应用中实现认证和授权功能。通过Spring Security的标签(如sec:authorize
)和Thymeleaf的模板引擎,可以在页面中实现动态的权限控制、用户认证等操作,增强了Web应用的安全性。