当前位置: 代码网 > it编程>编程语言>Java > Spring Security+MyBatis实现从数据库动态查询权限的完整实现方案

Spring Security+MyBatis实现从数据库动态查询权限的完整实现方案

2026年04月12日 Java 我要评论
一、整体思路spring security 的核心接口:userdetailsservice:根据用户名加载用户信息(密码、权限等)userdetails:用户对象,包含密码和grantedautho

一、整体思路

spring security 的核心接口:

  • userdetailsservice:根据用户名加载用户信息(密码、权限等)
  • userdetails:用户对象,包含密码和 grantedauthority
  • grantedauthority:权限标识(如 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数据库动态查询权限的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com