Spring Security的原理
- spring security的原理就是使用很多的拦截器对URL进行拦截,以此来管理登录验证和用户权限验证。
- 用户登陆,会被AuthenticationProcessingFilter(身份验证处理筛选器)拦截,调用AuthenticationManager(身份验证管理器)的实现,而且AuthenticationManager会调用ProviderManager(提供程序管理器)来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder(安全上下文持有者)中,以备后面访问资源时使用。所以要自定义用户的校验机制的话,只要实现自己的AuthenticationProvider就可以了。在用AuthenticationProvider 这个之前,我们需要提供一个获取用户信息的服务,实现 UserDetailsService 接口。
- 用户名密码->(Authentication(未认证) -> AuthenticationManager ->AuthenticationProvider->UserDetailService->UserDetails->Authentication(已认证)
步骤一:创建springboot项目,添加所需依赖
<?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 http://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.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-security-mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-security-mybatis</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-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
步骤二:mysql创建用户user表和角色role表
user表:
role表:
步骤三:修改application.yml配置![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/17f4bcbd860a9205e6ac2d0d5335e671.png)
步骤四:创建bean包,在该包下创建User和Role两个实体类
User类:
package com.example.demosecuritymybatis.bean;
import lombok.Data;
@Data
public class User {
private int id;
private String username; //账户
private String password; //密码
}
Role类:
package com.example.demosecuritymybatis.bean;
import lombok.Data;
@Data
public class Role {
private int id;
private String name; //角色名称
private String userId;
}
步骤五:创建mapper包,在该包下创建UserMapper和RoleMapper两个接口。在resources目录下创建mappers文件夹,创建UserMapper.xml和RoleMapper.xml
UserMapper接口:
@Repository
public interface UserMapper {
User selectUserByName(String username); //通过用户名查询用户
}
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demosecuritymybatis.mapper.UserMapper">
<select id="selectUserByName" parameterType="java.lang.String" resultType="com.example.demosecuritymybatis.bean.User">
select * from user where username=#{username}
</select>
</mapper>
RoleMapper接口:
@Repository
public interface RoleMapper {
List<Role> selectRoleByUserId(int userId); //通过用户id查询角色
}
RoleMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demosecuritymybatis.mapper.RoleMapper">
<select id="selectRoleByUserId" parameterType="java.lang.Integer" resultType="com.example.demosecuritymybatis.bean.Role">
select * from role where userId=#{userId}
</select>
</mapper>
application类需开启mapper扫描:
@MapperScan("com.example.demosecuritymybatis.mapper")
步骤六:创建controller包,在该包下创建TestController类
@Controller
public class TestController{
@RequestMapping("/")
public String index(){
return "index"; //一开始要访问的页面
}
@RequestMapping("/login")
public String login(){
return "login"; //登录页面
}
@RequestMapping("/fail")
public String fail(){
return "fail"; //登录失败页面
}
}
步骤七:在templates目录下创建login.html、index.html和fail.html页面
login.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
</head>
<body>
<div>
<h2>使用账号密码登录</h2>
<form name="form" th:action="@{/login}" action="/login" method="POST"> <div class="form-group">
<label>账号</label>
<input type="text" class="form-control" name="username" value="" placeholder="账号" />
</div>
<div class="form-group">
<label>密码</label>
<input type="password" class="form-control" name="password" placeholder="密码" />
</div>
<input type="submit" id="login" value="Login" />
</form>
</div>
</body>
</html>
index.html:
fail.html:
步骤八:创建security包,在该包下创建SecurityConfig类和WjcUserDetailsService类
SecurityConfig类:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService wjcUserDetailsService(){ //注册UserDetailsService 的bean
return new WjcUserDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//user Details Service验证
//密码加密,与数据库匹配
auth.userDetailsService(wjcUserDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //任何请求,登录后可以访问
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/fail")
.permitAll() //登录页面用户任意访问
.and()
.logout().permitAll(); //注销行为任意访问
}
}
WjcUserDetailsService类:
@Component
public class WjcUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
//复写此方法用于登录验证
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.selectUserByName(username); //获得用户
if (user==null){
throw new UsernameNotFoundException("用户名不存在");
}
//权限列表
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
//根据用户名获取角色列表
List<Role> roles = roleMapper.selectRoleByUserId(user.getId());
for (Role role:roles){
//循环角色,将角色名称放入到权限列表中
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getPassword(), authorities);
}
}
步骤九:给user表添加测试数据前,密码需要加密,方法如下
public class Main {
public static void main(String args[]){
BCryptPasswordEncoder bCryptPasswordEncoder =
new BCryptPasswordEncoder();
//加密"123"
String encode = bCryptPasswordEncoder.encode("123");
System.out.println(encode);
}
}