在本教程中,我将指导您如何编写代码,以使用具有基于表单的身份验证的Spring安全API来保护Spring Boot应用程序中的网页。用户详细信息存储在MySQL数据库中,并使用春季JDBC连接到数据库。我们将从本教程中的 ProductManager 项目开始,向现有的弹簧启动项目添加登录和注销功能。
1. 创建用户表和虚拟凭据
凭据应存储在数据库中,使用了Spring Data JPA 自动创建表,表间关系ER图如下:
2. 配置数据源属性
接下来,在应用程序属性文件中指定数据库连接信息,如下所示:根据您的MySQL数据库更新URL,用户名和密码。
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/product3?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#logging.level.root=WARN
3. 声明弹簧安全性和 MySQL JDBC 驱动程序的依赖关系
要将Spring安全API用于项目,请在pom.xml文件中声明以下依赖项:并且要将JDBC与弹簧启动和MySQL一起使用:请注意,依赖项版本已由弹簧启动初学者父项目定义。
4.0.0
org.springframework.boot
spring-boot-starter-parent
3.0.0
net.codejava
ProductManagerUserDetailsServiceAuditBoot3.0
2.0
ProductManagerUserDetailsServiceAuditBoot3.0
Spring Boot CRUD Web App Example
jar
1.8
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-security
org.thymeleaf.extras
thymeleaf-extras-springsecurity6
com.github.penggle
kaptcha
2.3.2
org.springframework.boot
spring-boot-starter-validation
org.projectlombok
lombok
1.18.24
provided
org.passay
passay
1.6.2
org.apache.commons
commons-text
1.10.0
org.hibernate
hibernate-envers
6.1.5.Final
org.springframework.boot
spring-boot-starter-test
test
junit
junit
4.13.2
org.springframework.boot
spring-boot-starter-actuator
javax.validation
validation-api
2.0.1.Final
org.springframework.boot
spring-boot-maven-plugin
4. 配置 Spring Security
要将 Spring 安全性与基于表单的身份验证和 CustomUserDetailsService 结合使用,请按如下方式创建 WebSecurityConfig 类:
package com.example;
import java.util.Arrays;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private DataSource dataSource;
@Autowired
private CustomLoginFailureHandler loginFailureHandler;
@Autowired
private CustomLoginSuccessHandler loginSuccessHandler;
@Autowired
private CustomUserDetailsService customUserDetailsService;
// @Autowired
// public void configAuthentication(AuthenticationManagerBuilder authBuilder) throws Exception {
// authBuilder.userDetailsService(customUserDetailsService)
// .passwordEncoder(new BCryptPasswordEncoder());
//
// }
//requestMatchers("/**").
@Bean
VerifyCodeAuthenticationProvider authenticationProvider() {
VerifyCodeAuthenticationProvider authenticationProvider = new VerifyCodeAuthenticationProvider();
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
authenticationProvider.setUserDetailsService(customUserDetailsService);
return authenticationProvider;
}
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(authenticationProvider()));
}
@Autowired
CustomAuthorizationManager customAuthorizationManager;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.requestMatchers("/403").permitAll()
.requestMatchers("/captcha_image").permitAll()
.requestMatchers("/header").permitAll()
.requestMatchers("/footer").permitAll()
.requestMatchers("/sidebar").permitAll()
.requestMatchers("/index2").permitAll()
.requestMatchers("/index3").permitAll()
.requestMatchers("/pages/**").permitAll()
.requestMatchers("/css/**").permitAll()
.requestMatchers("/js/**").permitAll()
.requestMatchers("/assets/**").permitAll()
.requestMatchers("/webjars/**").permitAll()
.requestMatchers("/common/**").permitAll()
.requestMatchers("/login").permitAll()
.requestMatchers("/logout").permitAll()
.requestMatchers("/verify").permitAll()
.requestMatchers("/home").authenticated()
.requestMatchers("/user/info").authenticated()
.requestMatchers("/change/password").authenticated()
.requestMatchers("/new/password").authenticated()
.requestMatchers("/").authenticated()
.anyRequest()
// .authenticated()
.access(customAuthorizationManager)
.and()
.formLogin().loginPage("/login")
.permitAll()
.failureHandler(loginFailureHandler)
.successHandler(loginSuccessHandler)
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/403");
http
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/")
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry());
return http.build();
}
@Bean
public SessionRegistry sessionRegistry() {
SessionRegistry sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
// Register HttpSessionEventPublisher
@Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
@Bean
BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
此安全配置类必须使用@EnableWebSecurity注释进行批注,并且是 Web 安全配置器适配器的子类。
CustomUserDetailsService
package com.example;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
User domainUser = userRepository.findByUsername(userName);
if (domainUser != null) {
return new org.springframework.security.core.userdetails.User(
domainUser.getUsername(),
domainUser.getPassword(),
domainUser.getEnabled(),
domainUser.getAccountNonExpired(),
domainUser.getCredentialsNonExpired(),
domainUser.getAccountNonLocked(),
getAuthorities(domainUser.getRoles())
);
}
// return null;
throw new UsernameNotFoundException("User " + userName + " does not exist");
}
private Collection extends GrantedAuthority> getAuthorities(
Collection roles) {
List authorities
= new ArrayList<>();
for (Role role : roles) {
// authorities.add(new SimpleGrantedAuthority(role.getName()));
role.getPermissions().stream()
.map(p -> new SimpleGrantedAuthority(p.getName()))
.forEach(authorities::add);
}
return authorities;
}
}
User
package com.example;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.envers.Audited;
@Data
@Entity
@DynamicUpdate
@Audited
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private Boolean enabled = true;
private Boolean accountNonExpired = true;
private Boolean credentialsNonExpired = true;
private Boolean accountNonLocked = true;
private String email;
private String name;
private String homepage;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set roles = new HashSet<>();
@Column(name = "password_changed_time")
private Date passwordChangedTime;
@Column(name = "failed_attempt")
private int failedAttempt;
@Column(name = "lock_time")
private Date lockTime;
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "user_history",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "history_id")
)
private Set historys = new HashSet<>();
}
角色和权限同时起作用
5.登录验证过程
package com.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String viewLoginPage() {
// custom logic before showing login page...
return "login";
}
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/403")
public String view403Page() {
return "403";
}
}
package com.example;
import com.google.code.kaptcha.Constants;
import java.util.Objects;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class VerifyCodeAuthenticationProvider extends DaoAuthenticationProvider {
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//获取当前请求
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String code = req.getParameter("kaptcha");//从当前请求中拿到code参数
System.out.println("!!!code=" + code);
String verifyCode = (String) req.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);//从session中获取生成的验证码字符串
System.out.println("!!!" + Constants.KAPTCHA_SESSION_KEY + "=" + verifyCode);
//比较验证码是否相同
if (StringUtils.isBlank(code) || StringUtils.isBlank(verifyCode) || !Objects.equals(code, verifyCode)) {
throw new AuthenticationServiceException("验证码错误!");
}
super.additionalAuthenticationChecks(userDetails, authentication);//调用父类DaoAuthenticationProvider的方法做密码的校验
}
}
kaptcha验证码
package com.example;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
/**
* @author will
*/
@Controller
public class KaptchaController {
@Autowired
private Producer captchaProducer;
@GetMapping("/captcha_image")
public void getKaptchaImage(HttpServletResponse response, HttpSession session) throws Exception {
response.setDateHeader("Expires", 0);
// Set standard HTTP/1.1 no-cache headers.
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
// Set standard HTTP/1.0 no-cache header.
response.setHeader("Pragma", "no-cache");
// return a jpeg
response.setContentType("image/jpeg");
// create the text for the image
String capText = captchaProducer.createText();
// store the text in the session
// request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
// save captcha to session
session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
// create the image with the text
BufferedImage bi = captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
// write the data out
ImageIO.write(bi, "jpg", out);
try {
out.flush();
} finally {
out.close();
}
}
}
package com.example;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
public class KaptchaConfig {
@Bean
public DefaultKaptcha getDefaultKaptcha() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.put("kaptcha.border", "no");
properties.put("kaptcha.textproducer.font.color", "black");
properties.put("kaptcha.image.width", "150");
properties.put("kaptcha.image.height", "40");
properties.put("kaptcha.textproducer.font.size", "30");
properties.put("kaptcha.session.key", "verifyCode");
properties.put("kaptcha.textproducer.char.space", "5");
// properties.setProperty("kaptcha.textproducer.char.length", "4");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
6.登录页面
Bootstrap 5 Sign In Form with Image Example
5. 测试登录和注销
启动Spring Boot应用程序并访问 http://localhost:8080 在Web浏览器中,您将看到自定义的登录页面出现:
现在输入正确的用户名admin和密码admin,您将看到主页如下:
并注意欢迎消息后跟用户名。用户现在已通过身份验证以使用该应用程序。单击“注销”按钮,您将看到自定义的登录页面出现,这意味着我们已成功实现登录并注销到我们的Spring Boot应用程序。
自定义从数据库中获取动态权限验证
package com.example;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.envers.Audited;
@Data
@Entity
@DynamicUpdate
@Audited
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private String uri;
private String method;
@Override
public String toString() {
return this.name;
}
}
package com.example;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PermissionRepository extends JpaRepository {
public Permission findByName(String name);
}
package com.example;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
@Component
public class CustomAuthorizationManager implements AuthorizationManager {
@Autowired
private PermissionRepository permissionRepository;
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext requestAuthorizationContext) {
HttpServletRequest request = requestAuthorizationContext.getRequest();
//获取用户认证信息
System.out.println(authentication.get().getAuthorities());
Object principal = authentication.get().getPrincipal();
System.out.println(principal.getClass());
//判断数据是否为空 以及类型是否正确
if (null != principal && principal instanceof org.springframework.security.core.userdetails.User) {
String username = ((org.springframework.security.core.userdetails.User) principal).getUsername();
System.out.println(username);
}
String requestURI = request.getRequestURI();
System.out.println(requestURI);
String method = request.getMethod();
System.out.println(method);
Collection extends GrantedAuthority> authorities = authentication.get().getAuthorities();
boolean hasPermission = false;
for (GrantedAuthority authority : authorities) {
String authorityname = authority.getAuthority();
System.out.println(authority.getAuthority());
Permission permission = permissionRepository.findByName(authorityname);
System.out.println(permissionRepository.findByName(authorityname));
if (null != permission && permission.getMethod().equals(request.getMethod()) && antPathMatcher.match(permission.getUri(), request.getRequestURI())) {
hasPermission = true;
break;
}
}
System.out.println(hasPermission);
return new AuthorizationDecision(hasPermission);
}
}
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Date;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
@Component
public class CustomLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Autowired
private UserLoginService userLoginService;
@Autowired
private UserRepository userRepository;
@Autowired
private LoginLogRepository loginLogRepository;
private static final long PASSWORD_EXPIRATION_TIME = 30L * 24L * 60L * 60L * 1000L; // 30 days
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String username = userDetails.getUsername();
User user = userRepository.getByUsername(username);
if (user.getFailedAttempt() > 0) {
userLoginService.resetFailedAttempts(user.getUsername());
}
System.out.println(request.getRemoteAddr());
System.out.println(request.getSession().getId());
LoginLog loginLog = new LoginLog();
loginLog.setUsername(username);
loginLog.setDescription("登录成功");
loginLog.setIp(request.getRemoteAddr());
loginLog.setEventtime(new Date());
loginLog.setSessionid(request.getSession().getId());
loginLogRepository.save(loginLog);
if (user.getPasswordChangedTime() == null) {
String redirectURL = request.getContextPath() + "/change/password";
response.sendRedirect(redirectURL);
} else {
long currentTime = System.currentTimeMillis();
long lastChangedTime = user.getPasswordChangedTime().getTime();
if (currentTime > lastChangedTime + PASSWORD_EXPIRATION_TIME) {
System.out.println("User:" + user.getUsername() + ":password expired");
System.out.println("Last Time password changed:" + user.getPasswordChangedTime());
System.out.println("Current Time:" + new Date());
String redirectURL = request.getContextPath() + "/change/password";
response.sendRedirect(redirectURL);
} else {
System.out.println(request.getContextPath() + user.getHomepage());
response.sendRedirect(request.getContextPath() + user.getHomepage());
// super.onAuthenticationSuccess(request, response, authentication);
}
}
}
}
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import org.springframework.security.authentication.CredentialsExpiredException;
@Component
public class CustomLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Autowired
private UserRepository userRepository;
@Autowired
private UserLoginService userLoginService;
@Autowired
private LoginLogRepository loginLogRepository;
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if (exception instanceof CredentialsExpiredException) {
// 保存异常信息到会话属性,供页面显示
saveException(request, exception);
String userName = request.getParameter("username");
// 跳转到修改密码页面
response.sendRedirect("/changepassword?error&username=" + userName);
}
String username = request.getParameter("username");
User user = userRepository.getByUsername(username);
if (user != null) {
LoginLog loginLog = new LoginLog();
loginLog.setUsername(username);
loginLog.setDescription("登录失败");
loginLog.setIp(request.getRemoteAddr());
loginLog.setEventtime(new Date());
loginLog.setSessionid(request.getSession().getId());
loginLogRepository.save(loginLog);
if (user.getEnabled() && user.getAccountNonLocked()) {
if (user.getFailedAttempt() < userLoginService.MAX_FAILED_ATTEMPTS - 1) {
System.out.println("user.getFailedAttempt()=" + user.getFailedAttempt());
userLoginService.increaseFailedAttempts(user);
} else {
userLoginService.lock(user);
exception = new LockedException("your account has been locked due to 3 failed attempt"
+ " It will be unclocked after 15 minutes");
System.out.println(exception);
System.out.println("userLoginService.lock(user)");
}
} else if (!user.getAccountNonLocked()) {
if (userLoginService.unlockWhenTimeExpired(user)) {
exception = new LockedException("your account has been unclock ."
+ " please try to login again");
System.out.println(exception);
}
}
}
super.setDefaultFailureUrl("/login?error");
super.onAuthenticationFailure(request, response, exception);
}
}
thymeleaf视图文件中,根据权限显示连接菜单
Dashboard - Admin Bootstrap Template
ID
User Name
E-mail
Name
Home Page
Roles
Actions
User ID
User Name
E-mail
Name
Home Page
Roles
Thymeleaf集成Bootstrap 5 Free Admin Dashboard Template 1 - freeetemplates
header.html
Dashboard - Admin Bootstrap Template
footer.html
Dashboard - Admin Bootstrap Template
sidebar.html
Dashboard - Admin Bootstrap Template
list_user.html
Dashboard - Admin Bootstrap Template
ID
User Name
E-mail
Name
Home Page
Roles
Actions
User ID
User Name
E-mail
Name
Home Page
Roles
历史密码保存和重用检测
package com.example;
import org.apache.commons.text.similarity.LevenshteinDistance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.util.Date;
import java.util.List;
import java.util.Set;
@Controller
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/user/new")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new UserDTO());
return "user/new_user";
}
@GetMapping("/user")
public String listUsers(Model model) {
List listUsers = userRepository.findAll();
model.addAttribute("listUsers", listUsers);
return "user/list_user";
}
@GetMapping("/user/edit/{id}")
public String editUser(@PathVariable("id") Long id, Model model) {
User user = userService.get(id);
List listRoles = userService.listRoles();
model.addAttribute("user", user);
model.addAttribute("listRoles", listRoles);
return "user/edit_user";
}
@PostMapping("/user/save")
public String saveUser(@Valid @ModelAttribute("user") UserDTO user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "user/new_user";
}
userService.saveUser(user);
return "redirect:/user";
}
@PostMapping("/user/update")
public String updateUser(User user) {
User repoUser = userRepository.findById(user.getId()).orElse(null);
if (repoUser != null) {
repoUser.setUsername(user.getUsername());
repoUser.setEmail(user.getEmail());
repoUser.setName(user.getName());
repoUser.setEnabled(user.getEnabled());
repoUser.setAccountNonLocked(user.getAccountNonLocked());
repoUser.setHomepage(user.getHomepage());
repoUser.setRoles(user.getRoles());
userRepository.save(repoUser);
}
return "redirect:/user";
}
@GetMapping("/user/resetpassword/{id}")
public String showResetPasswordForm(@PathVariable("id") Long id, Model model) {
User user = userRepository.findById(id).orElse(null);
UserResetPasswordDTO userResetPasswordDTO = new UserResetPasswordDTO();
if (user != null) {
userResetPasswordDTO.setId(user.getId());
userResetPasswordDTO.setUsername(user.getUsername());
model.addAttribute("user", userResetPasswordDTO);
return "user/reset_password";
}
return "redirect:/user";
}
@PostMapping("/user/savepassword")
public String savePassword(@Valid @ModelAttribute("user") UserResetPasswordDTO user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "user/reset_password";
}
User repoUser = userRepository.findById(user.getId()).orElse(null);
if (repoUser != null) {
repoUser.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
Date passwordChangedTime = new Date();
repoUser.setPasswordChangedTime(passwordChangedTime);
userRepository.save(repoUser);
}
return "redirect:/user";
}
@RequestMapping("/user/delete/{id}")
public String deleteUser(@PathVariable(name = "id") Long id) {
userRepository.deleteById(id);
return "redirect:/user";
}
@GetMapping("user/info")
public String userProfile(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.getName());
User user = userRepository.getByUsername(auth.getName());
model.addAttribute("user", user);
return "user/user_profile";
}
@GetMapping("/change/password")
public String changePassword(Model model) {
model.addAttribute("userDTO", new UserChangePasswordDTO());
return "user/password_update";
}
@PostMapping("/new/password")
public String
newPassword(@Valid @ModelAttribute("userDTO") UserChangePasswordDTO userChangePasswordDTO, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "user/password_update";
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (userChangePasswordDTO.getNewPass().equals(userChangePasswordDTO.getConfirmPass())) {
User user = userService.findUserByUsername(auth.getName());
boolean status = userService.isPasswordValid(userChangePasswordDTO.getPassword(), user.getPassword());
if (status) {
LevenshteinDistance levenshteinWithThreshold = new LevenshteinDistance(3);
// Returns -1 since the actual distance, 4, is higher than the threshold
System.out.println("Levenshtein distance: " + levenshteinWithThreshold.apply(userChangePasswordDTO.getNewPass(), userChangePasswordDTO.getPassword()));
if (levenshteinWithThreshold.apply(userChangePasswordDTO.getNewPass(), userChangePasswordDTO.getPassword()) == -1) {
Set setHistorysCheck = user.getHistorys();
boolean check = true;
for (History hist : setHistorysCheck) {
System.out.print(hist.getPassword());
if (bCryptPasswordEncoder.matches(userChangePasswordDTO.getNewPass(), hist.getPassword())) {
check = false;
break;
}
}
if (check) {
System.out.println(user.getHistorys());
History history = new History();
System.out.println("userChangePasswordDTO.getNewPass()=" + userChangePasswordDTO.getNewPass());
history.setPassword(bCryptPasswordEncoder.encode(userChangePasswordDTO.getNewPass()));
System.out.println(history);
Set setHistorys = user.getHistorys();
setHistorys.add(history);
user.setHistorys(setHistorys);
System.out.println(user.getHistorys());
userService.changePassword(user, userChangePasswordDTO);
return "login";
} else {
model.addAttribute("passMatched", "New password was same as history..!");
return "user/password_update";
}
} else {
model.addAttribute("passMatched", "New password need 4 diff with Current password..!");
return "user/password_update";
}
} else {
model.addAttribute("wrongPass", "Current password was wrong..!");
return "user/password_update";
}
} else {
model.addAttribute("passMatched", "Password doesn't matched..!");
return "user/password_update";
}
}
}
结论:
到目前为止,您已经学会了使用基于表单的身份验证和数据库内凭据来保护Spring Boot应用程序。您会看到 Spring 安全性使实现登录和注销功能变得非常容易,并且非常方便。为方便起见,您可以下载下面的示例项目。
源码:allwaysoft/ProductManagerUserDetailsServiceAuditBoot3.0 · GitHub