SpringSecurity权限管理框架系列(四)-Spring Security结合MySQL数据库实现自定义权限认证

1、自定义权限认证的实现思路

  • 自定义一个实体类LoginUser用来保存SygUser用户信息以及权限信息Set<String> permissions

  • 让这个LoginUser实体类实现Userdetails接口

  • 自定义的MyUserDetailsServiceImpl类的loadUserByUsername()方法中实现用户信息查询及该用户的权限信息查询

  • 自定义的MyUserDetailsServiceImpl类的loadUserByUsername()方法返回LoginUser实体类对象

2、代码实现以及中间出现的问题的解决方法

2.1 定义LoginUser实体类【初始定义,一会还会儿修改】

自定义登录用户实体类LoginUser如下,让其实现UserDetails接口

package com.yige.pojo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.Collection;
import java.util.Set;

/**
 * @Author: karma
 * @Date: 2022/3/21 0021 - 03 - 21 - 16:17
 * @Description: com.yige.pojo
 * @version: 1.0
 */
public class LoginUser implements UserDetails{

    /**
     * 权限列表
     */
    private Set<String> permissions;

    /**
     * 用户信息
     */
    private SygUser user;

    public LoginUser() {
    }

    public LoginUser(SygUser user, Set<String> permissions) {
        this.permissions = permissions;
        this.user = user;
    }

    public Set<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }

    public SygUser getUser() {
        return user;
    }

    public void setUser(SygUser user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @JsonIgnore
    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return null;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return false;
    }
}

2.2 修改MyUserDetailsServiceImpl类

修改loadUserByUsername(String userName)方法的实现逻辑,让其最后返回LoginUser实体类对象,具体实现如下

package com.yige.service.impl;

import com.yige.pojo.LoginUser;
import com.yige.pojo.SygUser;
import com.yige.service.SygUserSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;

@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SygUserSerivce userSerivce;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if(!StringUtils.hasLength(userName)) throw new UsernameNotFoundException("用户名不能为空");
        SygUser sygUser = userSerivce.selectUserByUserName(userName);
        if(null == sygUser) throw new UsernameNotFoundException("用户不存在");
        // 这里我们先不存数据库查权限信息,先给个空值,看看流程可不可以走通
        return createLoginUser(sygUser, new HashSet<String>());
    }

    public UserDetails createLoginUser(SygUser user, Set<String> permissions) {
        return new LoginUser(user, permissions);
    }
}

保存修改,重启项目,进行测试;

2.3 测试中遇到的问题及处理方法

使用账号student001,密码student001进行正常登录测试, 发现报错User account is locked, 这肯定不对,我们的账号是在数据库查出来的,状态肯定是正常启用的,没有被锁定,这是怎么回事?先说说怎么解决,后面再说为什么会这样

2.3.1 User account is locked异常

在这里插入图片描述
`User account is locked异常解决方法

修改你的实体类LoginUserisAccountNonLocked方法返回值为true即可
在这里插入图片描述修改之后
在这里插入图片描述
重新启动项目,再次测试

2.3.2 User is disabled异常

在这里插入图片描述
`User is disabled异常解决方法

修改你的实体类LoginUserisEnabled方法返回值为true即可
在这里插入图片描述
重新启动项目,再次测试

2.3.3 User account has expired异常

在这里插入图片描述
`User account has expired异常解决方法

修改你的实体类LoginUserisAccountNonExpired方法返回值为true即可
在这里插入图片描述
重新启动项目,再次测试

2.3.4 Bad credentials异常

在这里插入图片描述
Bad credentials异常解决方法

修改你的实体类LoginUserisCredentialsNonExpired方法返回值为true即可
在这里插入图片描述
重新启动项目,再次测试,

2.3.5 Empty encoded password异常

发现还是报错Bad credentials,但是后台console打印了一条warn日志,说是Empty encoded password,说明我们提供的userDetails的实现类实现的getPassword方法无法正常获取到用户密码, 怎么处理?
在这里插入图片描述
Empty encoded password异常解决方法

修改你的实体类LoginUsergetPassword方法返回值为你数据库用户实体类对象的密码即可
在这里插入图片描述
重新启动项目,再次测试
使用账号student001,密码student001进行正常登录测试, 登录成功
使用账号manager,密码manager进行正常登录测试, 登录成功, OK, 基本流程走通
在这里插入图片描述

2.4 所有问题处理完成之后的LoginUser实体类的全貌

package com.yige.pojo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.Collection;
import java.util.Set;

public class LoginUser implements UserDetails {

    /**
     * 权限列表
     */
    private Set<String> permissions;

    /**
     * 用户信息
     */
    private SygUser user;

    public LoginUser() {
    }

    public LoginUser(SygUser user, Set<String> permissions) {
        this.permissions = permissions;
        this.user = user;
    }

    public Set<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }

    public SygUser getUser() {
        return user;
    }

    public void setUser(SygUser user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @JsonIgnore
    @Override
    public String getPassword() {
        return user.getPassword();
    }

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

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}

2.5 我是怎么知道修改哪些方法就能解决问题的?

其实一开始我也不知道怎么处理这些问题, 我也觉得很奇怪, 为什么就会报这种错误?
直到我想到LoginUser实现了UserDetails接口, 里面有很多需要子类去实现的方法, 我就去看看了一下UserDetails接口中关于这几个方法的注释, 一下就明白了, 我们一起看看
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上就是为什么要修改LoginUser实现类那几个重写方法的返回值的原因, 不知道我有没有说清楚,哈哈, 个人理解哈,不喜勿喷~~

3、自定义权限的代码实现

3.1 实现查询用户权限的接口逻辑

  • service层
package com.yige.service;

import com.yige.pojo.SygUser;

import java.util.HashSet;
import java.util.Set;

public interface SygPermissionService {

    /**
     * @description 获取登录用户的菜单数据权限
     * @param user 用户信息
     * @return 菜单权限信息
     */
    public Set<String> getMenuPermission(SygUser user);
}
  • service.impl层
package com.yige.service.impl;

import com.yige.mapper.SygPermissionMapper;
import com.yige.pojo.SygUser;
import com.yige.service.SygPermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

@Service
public class SygPermissionServiceImpl implements SygPermissionService {

    @Autowired
    private SygPermissionMapper permissionMapper;

    public Set<String> getMenuPermission(SygUser user) {
        Set<String> perms = new HashSet<>();
        perms.addAll(permissionMapper.selectMenuPermsByUserId(user.getUserId()));
        return perms;
    }
}
  • mapper层
package com.yige.mapper;

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface SygPermissionMapper {

    List<String> selectMenuPermsByUserId(@Param("userId") Long userId);
}
  • SygPermissionMapper.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.yige.mapper.SygPermissionMapper">

    <select id="selectMenuPermsByUserId" resultType="string">
        select
          bm.menu_perms
        from
          bs_user as bu
        left join
          bs_user_role as bur
        on
          bu.user_id = bur.role_id
        left join
          bs_role_permission as brp
        on
          bur.role_id = brp.role_id
        left join
          bs_menu as bm
        on
          bm.menu_id = brp.menu_id
        where
          bu.user_id = #{userId}
  </select>
</mapper>

3.2 修改MyUserDetailsServiceImpl类的实现逻辑

package com.yige.service.impl;

import com.yige.pojo.LoginUser;
import com.yige.pojo.SygUser;
import com.yige.service.SygPermissionService;
import com.yige.service.SygUserSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Set;


@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SygUserSerivce userSerivce;

    @Autowired
    private SygPermissionService permissionService;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if(!StringUtils.hasLength(userName)) throw new UsernameNotFoundException("用户名不能为空");
        SygUser sygUser = userSerivce.selectUserByUserName(userName);
        if(null == sygUser) throw new UsernameNotFoundException("用户不存在");
        return createLoginUser(sygUser, permissionService.getMenuPermission(sygUser));
    }

    public UserDetails createLoginUser(SygUser user, Set<String> permissions) {
        permissions.stream().forEach(System.out::println);
        return new LoginUser(user, permissions);
    }
}

重新启动项目,再次测试
使用账号student001,密码student001进行正常登录测试, 登录成功
在这里插入图片描述控制台打印用户student001的权限列表
在这里插入图片描述
使用账号manager,密码manager进行正常登录测试, 登录成功
在这里插入图片描述
控制台打印manager用户的权限列表
在这里插入图片描述
至此,自定义权限认证的逻辑就全部实现完成了, 起始回头自己在捋一捋, 会不会发现其实也不太复杂,流程很清晰,实现很简单。喜欢我的文章请多多关注哈, 大家一起进步~

4、下一篇主要内容

下一篇主要研究一下WebSecurityConfigurerAdapter这个配置类以及
自定义配置类SecurityConfig来演示一下Spring security提供的各个接口的的作用以及怎么使用,敬请关注~

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Security是一个强大的安全框架,它是基于Spring框架的安全解决方案。它提供了一种全面的安全服务,可以在Web请求级别和方法级别进行安全控制。Spring Security提供了身份验证、授权和攻击保护等方面的支持,同时也支持灵活的安全配置和扩展。 关于如何结合SpringBoot、MySQLSpringSecurity和Vue编写一套完整的权限框架,可以按照以下步骤进行: 1、在SpringBoot中集成Spring SecurityMySQL:通过添加相应的依赖,配置数据源和Spring Security配置文件,以及实现UserDetailsService接口和PasswordEncoder接口对用户进行认证和授权。 2、编写Vue前端页面:使用Vue.js可以快速地构建一个前端单页应用,通过Vue Router和axios实现页面跳转和数据交互。 3、实现用户、角色和权限管理功能:在后端,实现用户、角色和权限的CRUD操作,同时结合Spring Security实现用户与URL的权限控制;在前端,通过Vue组件和API调用,实现相应的页面展示和操作。 4、实现Spring Security自定义认证和授权:可以通过实现AuthenticationProvider接口和AccessDecisionVoter接口,对用户自定义认证和授权进行处理。 5、通过JWT实现无状态认证:使用JwtTokenStore和JwtTokenEnhancer等相关组件,实现无状态认证和授权,提高Web应用的性能和扩展性。 以上就是一个简单的SpringBoot+MySQL+Spring Security+Vue权限框架实现的基本思路,可以根据实际项目需求做出适当的调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值