spring security 数据库验证

it2023-10-11  71

前一篇文章讲解了spring security配置自定义登录页面,而登录所需要的用户名和密码是在配置文件中设置的,而在实际开发中,我们希望的是用户和权限信息在数据库中。那么验证就需要通过数据库,本文继续沿用上一篇的案例,主要讲解通过数据库进行验证。(为了方便测试就不查询数据库啦,我们new一个User~)

pom文件

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.sunyuqi</groupId> <artifactId>springboot-sercurity-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-sercurity-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <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-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

前端页面

均放在templates目录下

登录页面login.html

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example </title> </head> <body> <div th:if="${param.error}"> 用户名或密码错误 </div> <form action="" method="post"> <div><label> 账号 : <input type="text" name="username"/> </label></div> <div><label> 密码 : <input type="password" name="password"/> </label></div> <div><input type="submit" value="登录"/></div> </form> </body> </html>

index.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>你好 <span th:text="${username}"></span></h3> <button type="button" onclick="window.location='/dologout'">登出</button> </body> </html>

controller

package com.sunyuqi.controller; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller public class UserController { @RequestMapping("/user/index") public String index(Authentication authentication,Model model){ model.addAttribute("username",authentication.getName()); return "index"; } /** * 登录页面 * @return */ @RequestMapping("/login") public String login(){ return "login"; } /** * 登出 * @param request * @param response * @param model * @return */ @RequestMapping({"/dologout"}) public String logout(HttpServletRequest request, HttpServletResponse response,Model model) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) {//清除认证 new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/login"; } }

User实体类

package com.sunyuqi.pojo; import java.util.List; public class User { private Long userId; private String username; private String sex; private String password; private List<String> roles; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<String> getRoles() { return roles; } public void setRoles(List<String> roles) { this.roles = roles; } @Override public String toString() { return "User{" + "userId=" + userId + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", password='" + password + '\'' + ", roles=" + roles + '}'; } }

security config

该配置了自定义登录页面,自定义登录校验等… 我们还设置了/user这个路径下的页面需要user权限

package com.sunyuqi.config; import com.sunyuqi.service.MyUserDetailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailService userDetailService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.authorizeRequests() // 设置权限 .antMatchers("/user/**").hasAnyRole("admin", "user") .anyRequest().authenticated() .and().httpBasic().and() // 默认登录页面 .formLogin().loginPage("/login") // 默认登录成功页面 .defaultSuccessUrl("/user/index") // 登录失败跳转 .failureForwardUrl("/login?error") .permitAll() .and().logout() .logoutUrl("/dologout") .logoutSuccessUrl("/login"); } /** * 添加 UserDetailsService, 实现自定义登录校验 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth // 从数据库读取的用户进行身份认证 .userDetailsService(userDetailService) .passwordEncoder(passwordEncoder()); } /** * 密码加密 */ @Bean public PasswordEncoder passwordEncoder(){ // 使用BCrypt加密密码 return new BCryptPasswordEncoder(); } }

MyUserDetailService

自定义登录校验需要我们实现UserDetailService接口 该类中我们设置的登录用户名为zhangsan,密码为qwe123,权限为user(实际开发中是要查询数据库的)

package com.sunyuqi.service; import com.sunyuqi.pojo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import java.util.ArrayList; @Component public class MyUserDetailService implements UserDetailsService { private static final Logger LOGGER = LoggerFactory.getLogger(MyUserDetailService.class); @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { LOGGER.info("对用户 [{}] 进行信息加载...",username); /* 从数据库里面查出用户 */ User user = new User(); user.setUserId(1L); user.setUsername("zhangsan"); user.setPassword(passwordEncoder.encode("qwe123")); user.setSex("男"); ArrayList<String> roles = new ArrayList<>(); roles.add("ROLE_user"); user.setRoles(roles); if(user==null){ LOGGER.error("用户 [{}] 未找到",username); throw new UsernameNotFoundException("Username:["+username+"] not found"); } LOGGER.info("用户 [{}] 信息加载完成",username); // SecurityUser 实现UserDetails return new SecurityUser(user); } }

loadUserByUsername需要返回一个 UserDetails接口,我们新建一个类实现该接口

SecurityUser

package com.sunyuqi.service; import com.sunyuqi.pojo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class SecurityUser extends User implements UserDetails { private static final Logger log = LoggerFactory.getLogger(SecurityUser.class); SecurityUser(User user){ if (user != null){ this.setUserId(user.getUserId()); this.setUsername(user.getUsername()); this.setSex(user.getSex()); this.setPassword(user.getPassword()); this.setRoles(user.getRoles()); } } /** * 权限集合 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); List<String> roles = this.getRoles(); if(roles != null){ for (String role : roles) { SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role); authorities.add(authority); } } log.info("获取登录用户已具有的权限:{}", authorities.toString()); return authorities; } /** * 指示用户的账户是否已过期。无法验证过期的账户。 * @return 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false */ @Override public boolean isAccountNonExpired() { return true; } /** * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。 * @return 如果用户未被锁定,则返回true,否则返回false */ @Override public boolean isAccountNonLocked() { return true; } /** * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证 * @return 如果用户的凭证有效(即未过期),则返回true * 如果不在有效(即过期),则返回false */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证 * @return 如果启用了用户,则返回true,否则返回false */ @Override public boolean isEnabled() { return true; } }

引导类

package com.sunyuqi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootSercurityDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootSercurityDemoApplication.class, args); } }

运行项目,访问http://localhost:8080/login 输入我们设置的用户名和密码,登录成功,同时因为该用户赋予了user权限,可以访问/user路径下的index页面

最新回复(0)