Spring Security实现动态权限设置(一)——基于数据库登录

本文详细介绍了使用SpringSecurity实现基于数据库的用户登录过程,包括常见问题解决、实体类设计、服务实现、MyBatis集成及SpringSecurity配置。通过具体代码示例,帮助读者理解并实践数据库登录的完整流程。

在这一部分先介绍Spring Security基于数据库登录,因为动态权限设置也是要基于数据库的,而且也要在基于数据库登录上实现。

在介绍实现数据库登录之前,先摆出笔者在这里踩过两个坑:
①踩坑:控制台报错There is no PasswordEncoder mapped for the id "null"并且输入登录之后不会返回任何登录相关信息,而是再次跳回登录界面;
原因:Spring Security配置类没有加@Configuration注解;
网上关于Spring Security 5.0版本之后,如果基于数据库认证需要在config方法中加载用户路径时的写法:(网上说法:保证用户登录时使用bcrypt对密码进行处理再与数据库中的密码比对)
下面展示一些 config配置类中的写法:

//注入userDetailsService的实现类
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());

笔者Spring Security 5.2.2环境下试过后面.passwordEncoder(new BCryptPasswordEncoder())其实加不加都可以运行通过;

②最好是角色表role的name必须以“ROLE_”开头,例如:“ROLE_admin”可以避免后续很多坑;之前学习时,对数据库role表没有以“ROLE_”开头,是因为可以在User实体类中的getAuthorities方法中将“ROLE_”前缀写在代码中,但这次由于动态权限需要查询role表,没办法在代码中加“ROLE_”前缀,会出一些bug;

下面开始正文部分,本次项所需依赖:MyBatis、MySqlDriver、以及Spring Security和Spring Web。
设计数据库
基于数据库登录只需要user、role、user_role三张表,这里准备了五张表是为了后面动态权限设置所需。
user表在这里插入图片描述其中的password字段是密码为123的明文由BCryptPasswordEncoder加密过后的密文,BCryptPasswordEncoder加密相同的密码会得到不同的密文,这里笔者偷懒,三个密文是一样的,但并不妨碍登录。实际项目不可这样。
role表 在这里插入图片描述
menu表,路径表
在这里插入图片描述

user_role表,根据uid(用户id)可以查到对应的rid(角色id),根据rid可以在role表中查到角色;
在这里插入图片描述
role_menu表,根据mid(路径id)可以查到对应的rid(角色id)根据rid可以在role表中查到路径所需的角色;
在这里插入图片描述
编写实体类
编写Role、User连个实体类,Role实体类的写法与普通实体类写法无异,需要注意的是实体类中的成员变量要和数据库中的字段名一一对应,并必须有get、set方法。User实体类需要实现UserDetail接口,并实现接口中的方法。这里主要介绍User实体类的写法:

public class User implements UserDetails {
    private int id;
    private String username;
    private String password;
    private boolean enabled;
    private boolean locked;
    private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public int getId() {
        return id;
    }

    public void setId(int 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 setLocked(Boolean locked) {
        this.locked = locked;
    }

    @Override
    //获取当前用户角色
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role:roles
             ) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    //账户是否未过期,直接返回true表示账户未过期,也可以在数据库中添加该字段
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    //账户是否为被锁,这里和数据库中的locked字段刚好反义,所以取反
    public boolean isAccountNonLocked() {
        return !this.locked;
    }

    @Override
    //密码是否为过期,数据库中无该字段,直接返回true
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    //账户是否可用,从数据库中获取该字段
    public boolean isEnabled() {
        return this.enabled;
    }
}

UserService实现用户登录
UserService类实现用户基于数据库登录,需实现UserDetailService接口,并实现接口中的方法,由于使用MyBatis整合数据库,所以后面需要编写Mapper类实现Service中需要的方法,供Service调用:

@Service
public class UserService implements UserDetailsService {
    @Autowired
    UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if(user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        user.setRoles(userMapper.getUserRolesById(user.getId()));
        return user;
    }
}

Mapper接口和XML编写
Mapper接口和XML这一套就是MyBatis里的,负责和数据库打交道,编写SQL根据需求操作数据库。这里只用实现Service中需要的方法。在UserMapper接口写入需要实现的方法并XML中实现Service中需要的loadUserByUsernamegetUserRolesById两个方法,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="org.yc.security_dynamic.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="org.yc.security_dynamic.bean.User">
        select * from user where username=#{username}
    </select>
    <select id="getUserRolesById" resultType="org.yc.security_dynamic.bean.Role">
        select * from role where id in (select rid from user_role where uid=#{id})
    </select>
</mapper>

getUserRolesById方法获取用户角色需要先在user_role表中根据用户id查出rid,然后在role表中根据rid查出角色,上面使用了一个嵌套查询;
Spring Security配置类
编写Spring Security配置类继承自WebSecurityConfigurerAdapter,在config方法中由UserService加载用户信息,并提供密码编码类BCryptPasswordEncoder

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;
  
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
}

Controller类
最后,编写controller类,测试基于数据库登录。

@RestController
public class HelloController {
   @GetMapping("/hello")
    public String hello(){
        return "hello security!";
    }
}

到这里就实现了Spring Security基于数据库的登录;在浏览器端访问localhost:8080/hello接口就会自动跳转到Spring Security的默认登陆页面:
在这里插入图片描述
输入user表中任意用户及密码就能访问到/hello接口。这里所有用户都能访问/hello接口,因为没有配置权限。

到这里,一个简单的Spring Security基于数据库登陆就完成了,下期在这基础之上介绍动态权限设置。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值