SpringSecurity实现自定义数据源实战指南
作者:️X️
本文给大家介绍SpringSecurity实现自定义数据源的相关知识,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
Spring Security简介
Java中的安全框架通常是指解决Web应用安全问题的框架。自行实现Web应用的安全性并不容易,需要考虑不同的认证和授权机制、网络关键数据传输加密等多方面的问题。
Java中常用的安全框架有Spring Security和Shiro,这两个安全框架都提供了强大功能,可以很容易实现Web应用的很多安全防护。下面对这两个安全框架的特点进行讲解
Spring Security是Spring生态系统中重要的一员,是一个基于AOP思想和Servlet过滤器实现的安全框架,它提供了完善的认证机制和方法级的授权功能,是一款非常优秀的权限管理框架。
在进行安全管理的过程中都涉及权限管理的两个重要概念:Authentication(认证)
和Authorization(授权)。权限管理是指根据系统设置的安全规则或者安全策略,用户可以访问且只能访问自己被授权的资源。
实现权限管理通常需要三个对象,分别为用户、角色、权限,这三个对象的说明如下。
- 用户:主要包含用户名、密码和当前用户的角色信息,可以实现认证操作。
- 角色:主要包含角色名称、角色描述和当前角色拥有的权限信息,可以实现授权操作。
- 权限:权限也可以称为菜单,主要包含当前权限名称、url地址等信息,可以实现动态展示菜单。
一、SQL数据表的设计
-- 创建用户表
CREATE TABLE `user` (
-- 主键id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
-- 用户名字段
`username` VARCHAR(32) DEFAULT NULL COMMENT '用户名',
-- 密码字段(建议存储加密后的密码)
`password` VARCHAR(255) DEFAULT NULL COMMENT '密码',
-- 账户启用状态:1=启用,0=禁用
`enabled` TINYINT(1) DEFAULT NULL COMMENT '是否可用',
-- 账户未过期标志:1=未过期,0=已过期
`accountNonExpired` TINYINT(1) DEFAULT NULL COMMENT '账户过期',
-- 账户未锁定标志:1=未锁定,0=已锁定
`accountNonLocked` TINYINT(1) DEFAULT NULL COMMENT '账户锁定',
-- 凭证未过期标志:1=未过期,0=已过期
`credentialsNonExpired` TINYINT(1) DEFAULT NULL COMMENT '凭证过期',
-- 设置主键
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 创建角色表(使用roles避免与MySQL关键字冲突)
CREATE TABLE `roles` (
-- 主键id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT,
-- 角色英文名,如ROLE_ADMIN
`name` VARCHAR(32) DEFAULT NULL,
-- 角色中文名,如管理员
`name_zh` VARCHAR(32) DEFAULT NULL,
-- 设置主键
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- 创建用户角色关联表(多对多关系)
CREATE TABLE `user_role` (
-- 主键id,自增
`id` INT(11) NOT NULL AUTO_INCREMENT,
-- 用户id,关联user表
`uid` INT(11) DEFAULT NULL,
-- 角色id,关联roles表
`rid` INT(11) DEFAULT NULL,
-- 设置主键
PRIMARY KEY (`id`),
-- 为uid字段创建索引,提高查询效率
KEY `uid` (`uid`),
-- 为rid字段创建索引,提高查询效率
KEY `rid` (`rid`),
-- 外键约束:uid引用user表的id字段
CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
-- 外键约束:rid引用roles表的id字段
CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- ==================== 第一步:插入用户数据 ====================
-- 插入3个测试用户
INSERT INTO `user` (
`username`,
`password`,
`enabled`,
`accountNonExpired`,
`accountNonLocked`,
`credentialsNonExpired`
) VALUES
-- 管理员用户:全部状态正常
('admin', 'admin123', 1, 1, 1, 1),
-- 普通用户:正常可用
('zhangsan', '123456', 1, 1, 1, 1),
-- 被禁用的用户:用于测试禁用功能
('lisi', '123456', 0, 1, 1, 1);
-- ==================== 第二步:插入角色数据 ====================
-- 插入几个常用角色
INSERT INTO `roles` (`name`, `name_zh`) VALUES
('ROLE_ADMIN', '管理员'),
('ROLE_USER', '普通用户'),
('ROLE_GUEST', '访客');
-- ==================== 第三步:插入用户角色关联 ====================
-- 给用户分配角色
INSERT INTO `user_role` (`uid`, `rid`) VALUES
-- admin用户:同时拥有管理员和普通用户角色
(1, 1), -- admin -> ROLE_ADMIN
(1, 2), -- admin -> ROLE_USER
-- zhangsan用户:只有普通用户角色
(2, 2), -- zhangsan -> ROLE_USER
-- lisi用户:只有访客角色
(3, 3); -- lisi -> ROLE_GUEST二、引入Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>三、配置yml文件
spring:
application:
name: SpringSecurity
datasource:
url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: com.example.springsecurity.mapper/**/*.xml四、编写实体类、mapper.xml、mapper层
package com.example.springsecurity.entity;
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Roles {
private Integer id;
private String name;
private String nameZh;
}package com.example.springsecurity.entity;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.*;
@ToString
// 自定义用户User
public class User implements UserDetails {/*这里继承UserDetails是因为SpringSecurity低层只认这个,相当于不良人只认大帅不认令牌*/
private Integer id; // 用户id
private String username; // 用户名
private String password; // 密码
private boolean enabled; // 是否可用
private boolean accountNonExpired; // 账户过期
private boolean accountNonLocked; // 账户锁定
private boolean credentialsNonExpired; // 凭证过期
private List<Roles> roles = new ArrayList<>(); // 用户角色信息
// 返回权限信息
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
roles.forEach(role -> {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(simpleGrantedAuthority);
});
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setRoles(List<Roles> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public List<Roles> getRoles() {
return roles;
}
}<?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.springsecurity.mapper.RolesMapper">
<select id="queryRolesByName" resultType="com.example.springsecurity.entity.Roles">
select
r.id,
r.name,
r.name_zh
from
roles r,user_role `ur`
where
r.id = `ur`.rid
and
ur.uid = #{uid}
</select>
</mapper><?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.springsecurity.mapper.UserMapper">
<select id="queryUserByName" resultType="com.example.springsecurity.entity.User">
select * from user <if test="username !=null and username !=''">
<where>
username = #{username}
</where>
limit 1
</if>
</select>
</mapper>public interface RolesMapper {
List<Roles> queryRolesByName(@Param("uid") Integer uid);
}public interface UserMapper {
User queryUserByName(@Param("username") String username);
}五、编写service层
@Service
@RequiredArgsConstructor//lombok自动生成构造函数
public class MyDetailsService implements UserDetailsService {
private final UserMapper userMapper;
private final RolesMapper rolesMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("username:========"+username);
User user = userMapper.queryUserByName(username);
if(ObjectUtil.isEmpty(user))
{
throw new UsernameNotFoundException("用户不存在");
}
//这里因为没有配置密码格式,后台数据库如果是明文的话,就需要用这个暂时“过渡”一下
user.setPassword("{noop}" + user.getPassword());
List<Roles> roles = rolesMapper.queryRolesByName(user.getId());
user.setRoles(roles);
return user;
}
}六、测试运行
到此这篇关于SpringSecurity实现自定义数据源的文章就介绍到这了,更多相关SpringSecurity自定义数据源内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
