前一篇文章讲解了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/>
</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";
}
@RequestMapping("/login")
public String
login(){
return "login";
}
@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");
}
@Override
protected void configure(AuthenticationManagerBuilder auth
) throws Exception
{
auth
.userDetailsService(userDetailService
)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder
passwordEncoder(){
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
);
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());
}
}
@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
;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@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页面