一、整体思路
spring security 的核心接口:
userdetailsservice:根据用户名加载用户信息(密码、权限等)userdetails:用户对象,包含密码和grantedauthoritygrantedauthority:权限标识(如role_admin或system:user:delete)
流程:
登录 → userdetailsservice.loaduserbyusername() → 查数据库 → 返回 userdetails → security自动比对密码 → 把权限存入securitycontext。
二、数据库表设计(最简但完整)
-- 用户表 create table `user` ( `id` int primary key auto_increment, `username` varchar(50) not null, `password` varchar(255) not null, -- bcrypt加密 `enabled` tinyint default 1 ); -- 角色表 create table `role` ( `id` int primary key auto_increment, `role_code` varchar(50) not null -- role_admin, role_user ); -- 权限表(细粒度权限) create table `permission` ( `id` int primary key auto_increment, `perm_code` varchar(100) not null -- user:add, user:delete, order:view ); -- 用户-角色关联 create table `user_role` ( `user_id` int, `role_id` int ); -- 角色-权限关联 create table `role_permission` ( `role_id` int, `perm_id` int );
示例数据:
-- 用户 admin / 123456(实际用bcrypt) insert into `user` values (1, 'admin', '$2a$10$nkmwpj7uowjqlqwjqlqwjq', 1); -- 角色 insert into `role` values (1, 'role_admin'), (2, 'role_user'); -- 权限 insert into `permission` values (1, 'user:list'), (2, 'user:delete'); -- 管理员拥有所有角色和权限 insert into `user_role` values (1,1); insert into `role_permission` values (1,1),(1,2);
三、mybatis 查询权限
1. 实体类
public class user {
private integer id;
private string username;
private string password;
private boolean enabled;
// getter/setter
}2. mapper 接口
@mapper
public interface usermapper {
@select("select id, username, password, enabled from user where username = #{username}")
user findbyusername(string username);
@select("select r.role_code from user_role ur " +
"join role r on ur.role_id = r.id " +
"where ur.user_id = #{userid}")
list<string> findrolesbyuserid(integer userid);
@select("select p.perm_code from user_role ur " +
"join role_permission rp on ur.role_id = rp.role_id " +
"join permission p on rp.perm_id = p.id " +
"where ur.user_id = #{userid}")
list<string> findpermissionsbyuserid(integer userid);
}四、核心:实现 userdetailsservice
@service
public class customuserdetailsservice implements userdetailsservice {
@autowired
private usermapper usermapper;
@override
public userdetails loaduserbyusername(string username) throws usernamenotfoundexception {
// 1. 查用户
user user = usermapper.findbyusername(username);
if (user == null) {
throw new usernamenotfoundexception("用户不存在: " + username);
}
// 2. 查角色(role_xxx)
list<string> roles = usermapper.findrolesbyuserid(user.getid());
// 3. 查权限(user:add 等)
list<string> permissions = usermapper.findpermissionsbyuserid(user.getid());
// 4. 合并权限 + 角色
// 注意:spring security 中角色需要加 role_ 前缀
set<grantedauthority> authorities = new hashset<>();
for (string role : roles) {
// 如果role已经是 role_admin 格式,直接加;否则自动补
if (role.startswith("role_")) {
authorities.add(new simplegrantedauthority(role));
} else {
authorities.add(new simplegrantedauthority("role_" + role));
}
}
for (string perm : permissions) {
authorities.add(new simplegrantedauthority(perm));
}
// 5. 返回 userdetails
return new org.springframework.security.core.userdetails.user(
user.getusername(),
user.getpassword(),
user.getenabled(), // 是否启用
true, // 账号未过期
true, // 凭证未过期
true, // 账号未锁定
authorities
);
}
}五、spring security 配置类
@configuration
@enablewebsecurity
public class securityconfig {
@autowired
private customuserdetailsservice userdetailsservice;
@bean
public passwordencoder passwordencoder() {
return new bcryptpasswordencoder(); // 密码加密
}
@bean
public authenticationmanager authmanager(httpsecurity http) throws exception {
daoauthenticationprovider provider = new daoauthenticationprovider();
provider.setuserdetailsservice(userdetailsservice);
provider.setpasswordencoder(passwordencoder());
return http.getsharedobject(authenticationmanagerbuilder.class)
.authenticationprovider(provider)
.build();
}
@bean
public securityfilterchain filterchain(httpsecurity http) throws exception {
http
.authorizehttprequests(auth -> auth
// 公开接口
.requestmatchers("/login", "/register").permitall()
// 权限控制:需要具体权限
.requestmatchers("/user/list").hasauthority("user:list")
.requestmatchers("/user/delete").hasauthority("user:delete")
// 角色控制
.requestmatchers("/admin/**").hasrole("admin")
// 其他请求需要认证
.anyrequest().authenticated()
)
.formlogin(form -> form
.loginpage("/login")
.defaultsuccessurl("/home")
.permitall()
)
.logout(logoutconfigurer::permitall)
.csrf(csrf -> csrf.disable()); // 根据实际情况开启
return http.build();
}
}六、在 controller 中使用
@restcontroller
public class usercontroller {
@getmapping("/user/list")
public string listusers() {
return "用户列表(需要 user:list 权限)";
}
@getmapping("/user/delete")
public string deleteuser() {
return "删除用户(需要 user:delete 权限)";
}
@getmapping("/current-permissions")
public authentication getcurrentauth() {
return securitycontextholder.getcontext().getauthentication();
}
}七、常见问题与优化
1. 缓存权限查询
不要在每次请求时重复查数据库(security 本身一次登录后权限存session)。
如果一定要动态刷新权限,可以:
// 修改权限后,重新加载 securitycontextholder.getcontext().setauthentication(updatedauth);
2. 使用 @preauthorize 更优雅
@preauthorize("hasauthority('user:delete')")
@deletemapping("/user/{id}")
public string delete(@pathvariable long id) {
return "删除成功";
}需要开启:
@enableglobalmethodsecurity(prepostenabled = true)
3. 性能优化:一次查询返回用户+角色+权限
用 mybatis 联表或 resultmap 一次性查出来,避免 n+1 问题。
<resultmap id="userwithauth" type="com.example.user">
<id column="id" property="id"/>
<collection property="roles" oftype="string" select="..."/>
</resultmap>八、总结:这套方案的特点
| 方面 | 说明 |
|---|---|
| ✅ 生产可用 | 绝大多数项目真实采用 |
| ✅ 灵活 | 权限可存数据库,随时调整 |
| ✅ 标准 | 完全遵循 spring security 接口设计 |
| ⚠️ 注意 | 密码必须用 bcrypt 等单向加密 |
| ⚠️ 注意 | 角色必须加 role_ 前缀(除非自定义) |
以上就是spring security+mybatis实现从数据库动态查询权限的完整实现方案的详细内容,更多关于spring security mybatis数据库动态查询权限的资料请关注代码网其它相关文章!
发表评论