基于SpringBoot整合SpringSecurity的认证授权(角色+权限)

之前一直是使用的Shiro,最近因为公司使用若依的前后端分离版本中,认证授权模块是使用的SpringSecurity,所以就打算写一遍这个教程了。嗯在这之前一直是使用Shiro做授权和认证的。嗯后面会讲的,在这之前读者需要具有SpringBoot基础、以及能够使用SpringBoot连接数据库进行操作,

前后端分离部分在这里
SpringSecurity+Jwt做前后端分离权限认证

源码下载链接在文末

先来说说RBAC模型

什么是RBAC

RBAC(全称:Role-Based Access Control)基于角色的权限访问控制,作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。

在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观
情况。

访问控制是针对越权使用资源的防御措施,目的是为了限制访问主体(如用户等) 对访问客体(如数据库资源等)的访问权限。企业环境中的访问控制策略大部分都采用基于角色的访问控制(RBAC)模型,是目前公认的解决大型企业的统一资源访问控制的有效方法

本案例的RBAC的设计思路

基于角色的访问控制基本原理是在用户和访问权限之间加入角色这一层,实现用户和权限的分离,用户只有通过激活角色才能获得访问权限。通过角色对权限分组,大大简化了用户权限分配表,间接地实现了对用户的分组,提高了权限的分配效率。且加入角色层后,访问控制机制更接近真实世界中的职业分配,便于权限管理。
在这里插入图片描述
下面我们开始

一、数据库建表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `pid` int(4) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `permission_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源名称',
  `str` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '资源标识符',
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户删除', 'sys:user:delete');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `rid` int(4) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `role_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
  PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_admin');
INSERT INTO `role` VALUES (2, 'ROLE_root');
INSERT INTO `role` VALUES (3, 'ROLE_test');

-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`  (
  `id` int(4) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `rid` int(4) NULL DEFAULT NULL COMMENT '角色id',
  `pid` int(4) NULL DEFAULT NULL COMMENT '权限id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `fk_rid2`(`rid`) USING BTREE,
  INDEX `fk_pid2`(`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES (1, 1, 1);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `uid` int(4) NOT NULL AUTO_INCREMENT COMMENT '账户id',
  `user_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `lock` int(1) NULL DEFAULT 0 COMMENT '是否可用 1可用 0不可用',
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zx', '123456', 0);
INSERT INTO `user` VALUES (2, 'te', '123', 0);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `uid` int(4) NULL DEFAULT NULL COMMENT '用户id',
  `rid` int(4) NULL DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `fk_uid`(`uid`) USING BTREE,
  INDEX `fk_rid`(`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);

SET FOREIGN_KEY_CHECKS = 1;

表结构分析

在这里插入图片描述

二、创建项目工程,并导入依赖

在这里插入图片描述
上面缺少了一个连接池依赖,在pom.xml文件中加入即可,当然不加也没有什么关系,我个人比较喜欢使用

 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
 </dependency>

创建好了之后,把一些没用的目录和文件删掉,
在这里插入图片描述
配置yml文件
在这里插入图片描述
然后根据数据库编写实体类,编写基本的CRUD方法 最后应该是这样子,因为类太多就不贴代码了,后面会提供源码下载
在这里插入图片描述

创建Controller

创建一个controller的包,在包里面创建一个TestController类,源码如下:

package work.zx.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {


    @RequestMapping("toLogin")
    public String toLogin(){
        System.out.println("去登录");
        return "login";
    }


    @PostMapping("/toMain")
    public String toMain(){
        System.out.println("去主页");
        return "main";
    }

    @PostMapping("/toError")
    public String toError() {
        System.out.println("去失败的页面");
        return "error.html";
    }
}

当然我们也建立了对应的静态页面。
在这里插入图片描述
login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/login" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="提交">
</form>
</body>
</html>

error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>失败</title>
</head>
<body>
登录失败
</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录炒年糕</title>
</head>
<body>
登录成功
</body>
</html>
下面来配置springSecurity

创建一个config的包,在里面创建SecurityConfig配置类,这个配置类要继承
WebSecurityConfigurerAdapter类
并且重写里面的void configure(HttpSecurity http) throws Exception方法
如下

package work.zx.Config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //自定义登录页
        http.formLogin()
                .loginPage("/toLogin") //登录页面
                .loginProcessingUrl("/login") // 登录提交的请求 ,必须和登录表单那边的action一致才行
                .successForwardUrl("/toMain") //登录成功后跳转页面 //跳转页面是在Controller中实现的
                .failureForwardUrl("/toError"); //登录失败的页面 //也是post请求

        //认证授权
        http.authorizeRequests()
                //开放登录请求
                .antMatchers("/toLogin").permitAll()
                .antMatchers("/toError").permitAll()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated();

        //关闭csrf 这个是跨站伪造攻击,关了就行
        http.csrf().disable();
    }

    @Bean
    public PasswordEncoder getPw(){
        return new BCryptPasswordEncoder();
    }
}

下面来自定义的登录逻辑

创建一个service包,在其下新建一个LoginUserService 这个类实现UserDetailsService 接口 重写其中的 UserDetails loadUserByUsername(String username)方法
== 注·:因为我考虑不周到,需要修改一下,我们实体类的名字 将User类 修改为 UserVo,因为SpinrgSecurity里面也有一个User类 产生冲突了,==
在这里插入图片描述

package work.zx.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import work.zx.dao.PermissionMapper;
import work.zx.dao.RoleMapper;
import work.zx.dao.UserMapper;
import work.zx.entity.Permission;
import work.zx.entity.Role;
import work.zx.entity.UserVo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

@Service
public class LoginUserService implements UserDetailsService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserMapper userMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //存放权限的
        Collection<GrantedAuthority> authorities = new ArrayList<>();

        System.out.println("自定义登录");
        //1、根据用户名去数据库查询,如果不存在抛出 UserNameNotFoundExeption异常
        UserVo userVo=userMapper.selectBYUserName(username);

        if(userVo==null){
            System.out.println("用户名不存在");
            throw new UsernameNotFoundException("用户名不存在");
        }
        //因为数据库中目前存放的是明文,一般在注册的时候都会加密,这里我就手动加密了
        String password=passwordEncoder.encode(userVo.getPassword());
       //拿到用户角色
        List<Role> roles=roleMapper.selectByUserId(userVo.getUid());
        for(Role role: roles){
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
            //拿到用户具有的权限
            List<Permission> permissionList=permissionMapper.selectByRoleId(role.getRid());
            for (Permission permission:permissionList){
                //添加到
                authorities.add(new SimpleGrantedAuthority(permission.getStr()));
            }
        }

        return new User(username,password,authorities);
    }
}

在这需要主注意一下,数据库中的角色名称 当然也可以称为角色标识符 必须以ROlE_为前缀
是Spring Security规定的,不要乱起哦。
在这里插入图片描述
最后是开启注解授权,也就是在Controller类上或者方法上加上注解即可授权那种
只需要在启动类上加一个注解即可
在这里插入图片描述

接下来我们编写几个接口测试一下角色和权限
如下:
在这里插入图片描述


    @GetMapping("/cs")
    @ResponseBody
    @PreAuthorize("hasAnyAuthority('sys:user:delete')")
    public String cs(){
        System.out.println("说明你具有delete权限");
        return "说明你具有delete权限";
    }

    @GetMapping("/test")
    @ResponseBody
    @PreAuthorize("hasRole('ROLE_admin')")
    public String test(){
        System.out.println("说明你具有ROLE_admin角色");
        return "说明你具有ROLE_admin角色";
    }

当然数据库中也准备了相应的测试数据,分别是 zx账号,具有需要的角色和权限, te账号,没有任何权限

1、先使用te账号登录访问

登录成功
在这里插入图片描述
访问cs接口失败
在这里插入图片描述
访问test接口失败
在这里插入图片描述

接下来是zx账号

权限测试成功
在这里插入图片描述
角色测试成功

在这里插入图片描述
那么到此,就完毕了
下期会出SpringBoot整合Shiro做授权和认证方面的案例、以及前后端分离的授权认证实现 关注我敬请期待哦
源码下载地址:

链接:https://pan.baidu.com/s/1vreO1Fpr1zC4wq7r2oDCgw
提取码:bbvc

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Spring Boot是一个用于构建独立的、生产级别的基于Spring的应用程序的框架,而Spring SecuritySpring的一个强大的安全框架,用于身份验证和授权。OAuth2是一种用于授权的开放标准,JWT是一种用于在用户和服务器之间传递安全信息的开放标准。这些技术的整合可以帮助我们构建一个完善的认证服务器,网关和微服务之间的权限认证系统。 首先,我们可以使用Spring Boot整合Spring Security来构建认证服务器。通过Spring Security提供的各种认证授权功能,我们可以实现用户的身份验证和授权管理。然后,结合OAuth2的开放标准,我们可以实现用户的授权码、密码、客户端凭证和刷新令牌等授权方式,从而提高系统的安全性和灵活性。 接着,我们可以利用Spring Boot搭建网关来统一管理微服务之间的访问权限。通过网关,我们可以对各个微服务进行统一的权限验证和访问控制,从而实现统一的安全管理和监控。 最后,我们可以在微服务中集成JWT来实现基于令牌的安全验证。JWT可以帮助我们在客户端和服务器之间传递安全信息,并通过签名和加密保障信息的安全性。 总之,通过Spring Boot整合Spring Security、OAuth2和JWT,我们可以构建一个完善的认证服务器、网关和微服务之间的权限认证系统,从而实现系统的安全可靠和权限灵活控制。这将有助于我们构建高效、安全的微服务架构,并为用户提供更加可靠的服务。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值