精华内容
下载资源
问答
  • 权限控制

    千次阅读 2019-01-22 20:09:03
    1.应用场景 ... RBAC //基于角色的权限控制 TBD 区别: TBD 各自应用场景: TBD 3.问题 TBD 4.参考 https://blog.csdn.net/u011277123/article/detai...

    1.应用场景

    开发权限管理模块, 更好地划分权限职责.

    2.学习/操作

    1. 介绍

    TBD

     

    2. 分类

    TBD

     

    3. 实现

    RBAC  //基于角色的权限控制

    TBD

     

    4. 区别/应用场景

    TBD

     

    后续补充

    ...

    3.问题

    TBD

    4.参考

    https://blog.csdn.net/jhkj_5154/article/details/79406721  //API权限控制
    http://baijiahao.baidu.com/s?id=1580846656488109967&wfr=spider&for=pc  //权限控制最常见的几种做法

    https://blog.csdn.net/u011277123/article/details/68940939  //基于角色和资源的用户权限控制(用SpringMVC实现)

    https://www.imooc.com/learn/799  //RBAC打造通用web管理权限

    http://www.kuitao8.com/20140518/2474.shtml  //yii2权限(RBAC)

    后续补充

    ...

     

    展开全文
  • Django权限控制

    万次阅读 2018-12-26 14:38:14
    自己搭建后台网站,需求:实现类似django Admin站点对每一张表的增删改查权限控制。 实现步骤: 1.权限控制Django框架已自带,共6张表,User表,Group表,UserGroup表,Permission表,GroupPermission表,User...

    自己搭建后台网站,需求:实现类似django Admin站点对每一张表的增删改查权限控制。

    实现步骤:

    1.权限控制Django框架已自带,共6张表,User表,Group表,UserGroup表,Permission表,GroupPermission表,UserPermission表,一般情况下,使用默认即可。

    2.若User表自定义,需继承Django自带AbstractUser类,Group表同理。

    3.创建类PermissionControl继承BasePermission,重写has_permission和has_object_permission方法。

    import re
    
    from django.contrib.auth.models import AnonymousUser
    from rest_framework.permissions import BasePermission
    
    
    class PermissionControl(BasePermission):
        """自定义权限控制类"""
    
        def has_permission(self, request, view):
            # 0.若用户未登陆直接访问,返回未授权
            if isinstance(request.user, AnonymousUser):
                return False
            permission_list = request.user.role.get_all_permissions()
            # 1.角色管理-角色
            if request.method == 'GET' and re.match(r'^/role_manage/roles/$', request.path):
                if 'view_role' in permission_list:
                    return True
                else:
                    return False
            elif request.method == 'POST' and re.match(r'^/role_manage/roles/$', request.path):
                if 'add_role' in permission_list:
                    return True
                else:
                    return False
            elif request.method == 'PUT' and re.match(r'^/role_manage/roles/\d+/$', request.path):
                if 'change_role' in permission_list:
                    return True
                else:
                    return False
            elif request.method == 'PATCH' and re.match(r'^/role_manage/roles/\d+/$', request.path):
                if 'change_role' in permission_list:
                    return True
                else:
                    return False
            elif request.method == 'DELETE' and re.match(r'^/role_manage/roles/\d+/$', request.path):
                if 'delete_role' in permission_list:
                    return True
                else:
                    return False
    
            # 同理,判断所有路由及权限
    
        def has_object_permission(self, request, view, obj):
            """GET请求单个对象时执行"""
            # 0.若用户未登陆直接访问,返回未授权
            if isinstance(request.user, AnonymousUser):
                return False
            permission_list = request.user.get_all_permissions()
            # 1.角色管理-角色
            if request.method == 'GET' and re.match(r'^/role_manage/roles/\d+/$', request.path):
                if 'view_role' in permission_list:
                    return True
                else:
                    return False
            # 同理,判断所有路由及权限

    4.视图中使用,本网站使用drf框架,仅需在要控制权限的类视图中加上permission_classes权限控制即可。

    class RoleViewSet(ModelViewSet):
        """
        list:
        查询所有角色
    
        read:
        根据角色id,查询角色
    
        create:
        创建角色
    
        delete:
        根据角色id,删除角色
    
        update:
        根据角色id,更新角色信息
    
        partial_update:
        根据角色id,部分更新角色信息
        """
        queryset = Role.objects.filter(is_delete=0).all()
        serializer_class = RoleSerializer
        permission_classes = [PermissionControl]
        filterset_class = RoleFilter
        pagination_class = StandarPageNumberPagination

    5.数据库中组(角色)权限表需添加角色权限,如:

     

     

     

    展开全文
  • Hbase权限控制

    万次阅读 2016-05-15 23:36:09
    Hbase的权限控制是通过AccessController Coprocessor协处理器框架实现的,可实现对用户的RWXCA的权限控制。 2 配置 配置hbase-site.xml CM主页→点击hbase(进入Hbase主页)→点击配置 1 点击左侧的Hbase服务范围启用...

    Hbase权限配置、使用手册

    1 Hbase权限控制简介

    Hbase的权限控制是通过AccessController Coprocessor协处理器框架实现的,可实现对用户的RWXCA的权限控制。

    2 配置

    配置hbase-site.xml

    CM主页→点击hbase(进入Hbase主页)→点击配置

    1 点击左侧的Hbase服务范围启用Hbase权限控制,设置Hbase超级用户hbase.superuser

     

    2 点击左侧的master在 hbase.coprocessor.master.classes里追加如下内容

    org.apache.hadoop.hbase.security.access.AccessController

    3 点击左侧的regionserver在hbase.coprocessor.region.classes里追加如下内容

    org.apache.hadoop.hbase.security.token.TokenProvider,org.apache.hadoop.hbase.security.access.AccessController

     

    配置文件附录:

    <property>

    <name>hbase.security.authorization</name>

    <value>true</value>

    </property>

    <property>

    <name>hbase.coprocessor.master.classes</name>

    <value>org.apache.hadoop.hbase.security.access.AccessController</value>

    </property>

    <property>

    <name>hbase.coprocessor.region.classes</name>

    <value>org.apache.hadoop.hbase.security.token.TokenProvider,org.apache.hadoop.hbase.security.access.AccessController</value>

    </property>

    <property>
    <name>hbase.superuser</name>
    <value>hbase,root,administrator</value>
    </property>

    3 使用

    3.1 hbase权限验证

    1 管理员用户Hbase下对Hbase进行赋权

    grant 'wcj','r'

    grant 'wcj' ,w'

    #授予用户shenl读、写表wcj的权限

    grant 'shenl', 'RW', 'wcj'

    #授予用户shenl对表wcj的列族f1的age只读权限

    grant 'shenl', 'R', 'wcj', 'f1', 'age'

    2 管理员用户Hbase下收回Hbase表的访问权限权限

    revoke ‘R’ ‘wcj’

    revoke ‘W’ ‘wcj’

    3 管理员下hbase权限

    user_permission ‘wcj’

     

    3.2 hbase权限介绍

    1 hbase权限与常用命令对照表

    ACLs

    Permissions

    Comment

    READ('R')

    Get, Scan, or Exists calls

    R denotes read permissions

    WRITE('W')

    Put, Delete, LockRow, UnlockRow, IncrementColumnValue,

    CheckAndDelete, CheckAndPut, Flush, & Compact

    W denotes write permissions

    EXEC('X')

    denotes execute permissions, which is required to execute coprocessor endpoints

    CREATE('C')

    Create, Alter, & Drop

    denotes create permissions

    ADMIN('A')

    Enable, Disable, Snapshot, Restore, Clone,Split, MajorCompact, Grant, Revoke, and Shutdown

    denotes admin permissions

    2 hbase权限命令

    grant <user> <permissions>[<table>[ <column family>[ <column qualifier> ] ] ]

    #grants permissions

    revoke <user> <permissions> [<table> [ <column family> [ <column qualifier> ] ] ]

    # revokes permissions

    user_permission <table> # displaysexisting permissions

    3现实版访问场景

    Job Title

    Scope

    Permissions

    Description

    Senior Administrator

    Global

    Access, Create

    Manages the cluster and gives access to Junior Administrators.

    Junior Administrator

    Global

    Create

    Creates tables and gives access to Table Administrators.

    Table Administrator

    Table

    Access

    Maintains a table from an operations point of view.

    Data Analyst

    Table

    Read

    Creates reports from HBase data.

    Web Application

    Table

    Read, Write

    Puts data into HBase and uses HBase data to perform operations.

    4 问题总结

    展开全文
  • Springboot + Spring Security 实现前后端分离登录认证及权限控制前言本文主要的功能文章目录一、数据库表设计建表语句初始化表数据语句二、Spring Security核心配置:WebSecurityConfig三、用户登录认证逻辑:...

    前言

        关于Spring Security的概念部分本文不进行赘述,本文主要针对于对Spring Security以及Springboot有一定了解的小伙伴,帮助大家使用Springboot + Spring Security 实现一个前后端分离登录认证的过程。
        文章会一步一步循序渐进的带大家敲一遍代码。最终的代码请看最后。
        代码中我用到了插件lombok来生成实体的getter/setter,如果不想装插件请自己补全getter/setter

    本文主要的功能

    1、前后端分离用户登录认证
    2、基于RBAC(角色)的权限控制

    文章目录

    1、准备工作
    2、数据库表设计
    3、Spring Security核心配置:WebSecurityConfig
    4、用户登录认证逻辑:UserDetailsService
    5、用户密码加密
    6、屏蔽Spring Security默认重定向登录页面以实现前后端分离功能
    7、实现登录成功/失败、登出处理逻辑
    8、会话管理(登录过时、限制单用户或多用户登录等)
    9、实现基于JDBC的动态权限控制
    10、结束语

    文章正文

    一、准备工作

    1、统一错误码枚举

    /**
     * @Author: Hutengfei
     * @Description: 返回码定义
     * 规定:
     * #1表示成功
     * #1001~1999 区间表示参数错误
     * #2001~2999 区间表示用户错误
     * #3001~3999 区间表示接口异常
     * @Date Create in 2019/7/22 19:28
     */
    public enum ResultCode {
        /* 成功 */
        SUCCESS(200, "成功"),
    
        /* 默认失败 */
        COMMON_FAIL(999, "失败"),
    
        /* 参数错误:1000~1999 */
        PARAM_NOT_VALID(1001, "参数无效"),
        PARAM_IS_BLANK(1002, "参数为空"),
        PARAM_TYPE_ERROR(1003, "参数类型错误"),
        PARAM_NOT_COMPLETE(1004, "参数缺失"),
    
        /* 用户错误 */
        USER_NOT_LOGIN(2001, "用户未登录"),
        USER_ACCOUNT_EXPIRED(2002, "账号已过期"),
        USER_CREDENTIALS_ERROR(2003, "密码错误"),
        USER_CREDENTIALS_EXPIRED(2004, "密码过期"),
        USER_ACCOUNT_DISABLE(2005, "账号不可用"),
        USER_ACCOUNT_LOCKED(2006, "账号被锁定"),
        USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),
        USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),
        USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"),
    
        /* 业务错误 */
        NO_PERMISSION(3001, "没有权限");
        private Integer code;
        private String message;
    
        ResultCode(Integer code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        /**
         * 根据code获取message
         *
         * @param code
         * @return
         */
        public static String getMessageByCode(Integer code) {
            for (ResultCode ele : values()) {
                if (ele.getCode().equals(code)) {
                    return ele.getMessage();
                }
            }
            return null;
        }
    }
    

    2、统一json返回体

    /**
     * @Author: Hutengfei
     * @Description: 统一返回实体
     * @Date Create in 2019/7/22 19:20
     */
    public class JsonResult<T> implements Serializable {
        private Boolean success;
        private Integer errorCode;
        private String errorMsg;
        private T data;
    
        public JsonResult() {
        }
    
        public JsonResult(boolean success) {
            this.success = success;
            this.errorCode = success ? ResultCode.SUCCESS.getCode() : ResultCode.COMMON_FAIL.getCode();
            this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : ResultCode.COMMON_FAIL.getMessage();
        }
    
        public JsonResult(boolean success, ResultCode resultEnum) {
            this.success = success;
            this.errorCode = success ? ResultCode.SUCCESS.getCode() : (resultEnum == null ? ResultCode.COMMON_FAIL.getCode() : resultEnum.getCode());
            this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : (resultEnum == null ? ResultCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
        }
    
        public JsonResult(boolean success, T data) {
            this.success = success;
            this.errorCode = success ? ResultCode.SUCCESS.getCode() : ResultCode.COMMON_FAIL.getCode();
            this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : ResultCode.COMMON_FAIL.getMessage();
            this.data = data;
        }
    
        public JsonResult(boolean success, ResultCode resultEnum, T data) {
            this.success = success;
            this.errorCode = success ? ResultCode.SUCCESS.getCode() : (resultEnum == null ? ResultCode.COMMON_FAIL.getCode() : resultEnum.getCode());
            this.errorMsg = success ? ResultCode.SUCCESS.getMessage() : (resultEnum == null ? ResultCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
            this.data = data;
        }
    
        public Boolean getSuccess() {
            return success;
        }
    
        public void setSuccess(Boolean success) {
            this.success = success;
        }
    
        public Integer getErrorCode() {
            return errorCode;
        }
    
        public void setErrorCode(Integer errorCode) {
            this.errorCode = errorCode;
        }
    
        public String getErrorMsg() {
            return errorMsg;
        }
    
        public void setErrorMsg(String errorMsg) {
            this.errorMsg = errorMsg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    

    3、返回体构造工具

    /**
     * @Author: Hutengfei
     * @Description:
     * @Date Create in 2019/7/22 19:52
     */
    public class ResultTool {
        public static JsonResult success() {
            return new JsonResult(true);
        }
    
        public static <T> JsonResult<T> success(T data) {
            return new JsonResult(true, data);
        }
    
        public static JsonResult fail() {
            return new JsonResult(false);
        }
    
        public static JsonResult fail(ResultCode resultEnum) {
            return new JsonResult(false, resultEnum);
        }
    }
    

    4、pom

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.7.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.spring</groupId>
        <artifactId>security</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>security</name>
        <description>测试spring-security工程</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring.security.version>5.1.6.RELEASE</spring.security.version>
            <fastjson.version>1.2.46</fastjson.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- spring-security -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <!-- Hikari连接池-->
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
                <exclusions>
                    <!-- 排除 tomcat-jdbc 以使用 HikariCP -->
                    <exclusion>
                        <groupId>org.apache.tomcat</groupId>
                        <artifactId>tomcat-jdbc</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- Mybatis-plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatisplus-spring-boot-starter</artifactId>
                <version>1.0.5</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus</artifactId>
                <version>2.1.9</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!--JSON-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.8.1</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    5、配置文件

    spring:
      application:
        name: isoftstone-security
      datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/spring_security?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=CTT
        username: root
        password: root
        hikari:
          minimum-idle: 5
          idle-timeout: 600000
          maximum-pool-size: 10
          auto-commit: true
          pool-name: MyHikariCP
          max-lifetime: 1800000
          connection-timeout: 30000
          connection-test-query: SELECT 1
    server:
      port: 8666
    
    mybatis-plus:
      # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
      # 如果是放在resource目录 classpath:/mapper/*Mapper.xml
      mapper-locations: classpath:mapper/*.xml, classpath:mybatis/mapping/**/*.xml
      #实体扫描,多个package用逗号或者分号分隔
      typeAliasesPackage: com.spring.**
      global-config:
        #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
        id-type: 0
        #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
        field-strategy: 1
        #驼峰下划线转换
        db-column-underline: true
        #刷新mapper 调试神器
        refresh-mapper: true
        #数据库大写下划线转换
        #capital-mode: true
        #序列接口实现类配置,不在推荐使用此方式进行配置,请使用自定义bean注入
        #key-generator: com.baomidou.mybatisplus.incrementer.H2KeyGenerator
        #逻辑删除配置(下面3个配置)
        logic-delete-value: 0
        logic-not-delete-value: 1
        #自定义sql注入器,不在推荐使用此方式进行配置,请使用自定义bean注入
        #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
        #自定义填充策略接口实现,不在推荐使用此方式进行配置,请使用自定义bean注入
        # meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
        #自定义SQL注入器
        #sql-injector: com.baomidou.springboot.xxx
        # SQL 解析缓存,开启后多租户 @SqlParser 注解生效
        sql-parser-cache: true
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false
    

    二、数据库表设计

    数据库设计

    建表语句

    create table sys_user
    (
        id                      int auto_increment
            primary key,
        account                 varchar(32)          not null comment '账号',
        user_name               varchar(32)          not null comment '用户名',
        password                varchar(64)          null comment '用户密码',
        last_login_time         datetime             null comment '上一次登录时间',
        enabled                 tinyint(1) default 1 null comment '账号是否可用。默认为1(可用)',
        not_expired             tinyint(1) default 1 null comment '是否过期。默认为1(没有过期)',
        account_not_locked      tinyint(1) default 1 null comment '账号是否锁定。默认为1(没有锁定)',
        credentials_not_expired tinyint(1) default 1 null comment '证书(密码)是否过期。默认为1(没有过期)',
        create_time             datetime             null comment '创建时间',
        update_time             datetime             null comment '修改时间',
        create_user             int                  null comment '创建人',
        update_user             int                  null comment '修改人'
    )
        comment '用户表';
    
    create table sys_role
    (
        id               int auto_increment comment '主键id'
            primary key,
        role_name        varchar(32) null comment '角色名',
        role_description varchar(64) null comment '角色说明'
    )
        comment '用户角色表';
    
    create table sys_permission
    (
        id              int auto_increment comment '主键id'
            primary key,
        permission_code varchar(32) null comment '权限code',
        permission_name varchar(32) null comment '权限名'
    )
        comment '权限表';
    
    create table sys_user_role_relation
    (
        id      int auto_increment comment '主键id'
            primary key,
        user_id int null comment '用户id',
        role_id int null comment '角色id'
    )
        comment '用户角色关联关系表';
    
    create table sys_role_permission_relation
    (
        id            int auto_increment comment '主键id'
            primary key,
        role_id       int null comment '角色id',
        permission_id int null comment '权限id'
    )
        comment '角色-权限关联关系表';
    
    create table sys_request_path
    (
        id          int auto_increment comment '主键id'
            primary key,
        url         varchar(64)  not null comment '请求路径',
        description varchar(128) null comment '路径描述'
    )
        comment '请求路径';
    
    create table sys_request_path_permission_relation
    (
        id            int null comment '主键id',
        url_id        int null comment '请求路径id',
        permission_id int null comment '权限id'
    )
        comment '路径权限关联表';
    

    初始化表数据语句

    -- 用户
    INSERT INTO sys_user (id, account, user_name, password, last_login_time, enabled, account_non_expired, account_non_locked, credentials_non_expired, create_time, update_time, create_user, update_user) VALUES (1, 'user1', '用户1', '$2a$10$47lsFAUlWixWG17Ca3M/r.EPJVIb7Tv26ZaxhzqN65nXVcAhHQM4i', '2019-09-04 20:25:36', 1, 1, 1, 1, '2019-08-29 06:28:36', '2019-09-04 20:25:36', 1, 1);
    INSERT INTO sys_user (id, account, user_name, password, last_login_time, enabled, account_non_expired, account_non_locked, credentials_non_expired, create_time, update_time, create_user, update_user) VALUES (2, 'user2', '用户2', '$2a$10$uSLAeON6HWrPbPCtyqPRj.hvZfeM.tiVDZm24/gRqm4opVze1cVvC', '2019-09-05 00:07:12', 1, 1, 1, 1, '2019-08-29 06:29:24', '2019-09-05 00:07:12', 1, 2);
    -- 角色
    INSERT INTO sys_role (id, role_code, role_name, role_description) VALUES (1, 'admin', '管理员', '管理员,拥有所有权限');
    INSERT INTO sys_role (id, role_code, role_name, role_description) VALUES (2, 'user', '普通用户', '普通用户,拥有部分权限');
    -- 权限
    INSERT INTO sys_permission (id, permission_code, permission_name) VALUES (1, 'create_user', '创建用户');
    INSERT INTO sys_permission (id, permission_code, permission_name) VALUES (2, 'query_user', '查看用户');
    INSERT INTO sys_permission (id, permission_code, permission_name) VALUES (3, 'delete_user', '删除用户');
    INSERT INTO sys_permission (id, permission_code, permission_name) VALUES (4, 'modify_user', '修改用户');
    -- 请求路径
    INSERT INTO sys_request_path (id, url, description) VALUES (1, '/getUser', '查询用户');
    -- 用户角色关联关系
    INSERT INTO sys_user_role_relation (id, user_id, role_id) VALUES (1, 1, 1);
    INSERT INTO sys_user_role_relation (id, user_id, role_id) VALUES (2, 2, 2);
    -- 角色权限关联关系
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (1, 1, 1);
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (2, 1, 2);
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (3, 1, 3);
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (4, 1, 4);
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (5, 2, 1);
    INSERT INTO sys_role_permission_relation (id, role_id, permission_id) VALUES (6, 2, 2);
    -- 请求路径权限关联关系
    INSERT INTO sys_request_path_permission_relation (id, url_id, permission_id) VALUES (null, 1, 2);
    

    三、Spring Security核心配置:WebSecurityConfig

        创建WebSecurityConfig继承WebSecurityConfigurerAdapter类,并实现configure(AuthenticationManagerBuilder auth)和 configure(HttpSecurity http)方法。后续我们会在里面加入一系列配置,包括配置认证方式、登入登出、异常处理、会话管理等。

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //配置认证方式等
            super.configure(auth);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //http相关的配置,包括登入登出、异常处理、会话管理等
            super.configure(http);
        }
    }
    

    四、用户登录认证逻辑:UserDetailsService

    1、创建自定义UserDetailsService

        这是实现自定义用户认证的核心逻辑,loadUserByUsername(String username)的参数就是登录时提交的用户名,返回类型是一个叫UserDetails 的接口,需要在这里构造出他的一个实现类User,这是Spring security提供的用户信息实体。

    public class UserDetailsServiceImpl  implements UserDetailsService {
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //需要构造出 org.springframework.security.core.userdetails.User 对象并返回
            return null;
        }
    }
    

        这里我们使用他的一个参数比较详细的构造函数,源码如下

    User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
    

    其中参数:

    String username:用户名
    String password: 密码
    boolean enabled: 账号是否可用
    boolean accountNonExpired:账号是否过期
    boolean credentialsNonExpired:密码是否过期
    boolean accountNonLocked:账号是否锁定
    Collection<? extends GrantedAuthority> authorities):用户权限列表

        这就与我们的创建的用户表的字段对应起来了,Spring security都为我们封装好了,如果用户信息的状态异常,登录时则会抛出相应的异常,根据捕获到的异常判断是什么原因(账号过期/密码过期/账号锁定等等…),进而就可以提示前台了。
        我们就按照该参数列表构造出我们所需要的数据,然后返回,就完成了基于JDBC的自定义用户认证。
        首先用户名密码以及用户状态信息都是从用户表里进行单表查询来的,而权限列表则是通过用户表、角色表以及权限表等关联查出来的,那么接下来就是准备service和dao层方法了

    2、准备service和dao层方法

    (1)根据用户名查询用户信息

    映射文件

        <!--根据用户名查询用户-->
        <select id="selectByName" resultMap="SysUserMap">
            select * from sys_user where account = #{userName};
        </select>
    

    service层

        /**
         * 根据用户名查询用户
         *
         * @param userName
         * @return
         */
        SysUser selectByName(String userName);
    

    (2)根据用户名查询用户的权限信息

    映射文件

        <select id="selectListByUser" resultMap="SysPermissionMap">
            SELECT
            p.*
            FROM
            sys_user AS u
            LEFT JOIN sys_user_role_relation AS ur
            ON u.id = ur.user_id
            LEFT JOIN sys_role AS r
            ON r.id = ur.role_id
            LEFT JOIN sys_role_permission_relation AS rp
            ON r.id = rp.role_id
            LEFT JOIN sys_permission AS p
            ON p.id = rp.permission_id
            WHERE u.id = #{userId}
        </select>
    

    service层

        /**
         * 查询用户的权限列表
         *
         * @param userId
         * @return
         */
        List<SysPermission> selectListByUser(Integer userId);
    

        这样的话流程我们就理清楚了,首先根据用户名查出对应用户,再拿得到的用户的用户id去查询它所拥有的的权限列表,最后构造出我们需要的org.springframework.security.core.userdetails.User对象。
    接下来改造一下刚刚自定义的UserDetailsService

    public class UserDetailsServiceImpl implements UserDetailsService {
        @Autowired
        private SysUserService sysUserService;
        @Autowired
        private SysPermissionService sysPermissionService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            if (username == null || "".equals(username)) {
                throw new RuntimeException("用户不能为空");
            }
            //根据用户名查询用户
            SysUser sysUser = sysUserService.selectByName(username);
            if (sysUser == null) {
                throw new RuntimeException("用户不存在");
            }
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            if (sysUser != null) {
                //获取该用户所拥有的权限
                List<SysPermission> sysPermissions = sysPermissionService.selectListByUser(sysUser.getId());
                // 声明用户授权
                sysPermissions.forEach(sysPermission -> {
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(sysPermission.getPermissionCode());
                    grantedAuthorities.add(grantedAuthority);
                });
            }
            return new User(sysUser.getAccount(), sysUser.getPassword(), sysUser.getEnabled(), sysUser.getAccountNonExpired(), sysUser.getCredentialsNonExpired(), sysUser.getAccountNonLocked(), grantedAuthorities);
        }
    }
    

        然后将我们的自定义的基于JDBC的用户认证在之前创建的WebSecurityConfig 中得configure(AuthenticationManagerBuilder auth)中声明一下,到此自定义的基于JDBC的用户认证就完成了

        @Bean
        public UserDetailsService userDetailsService() {
            //获取用户账号密码及权限信息
            return new UserDetailsServiceImpl();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            //配置认证方式
            auth.userDetailsService(userDetailsService());
        }
    

    五、用户密码加密

        新版本的Spring security规定必须设置一个默认的加密方式,不允许使用明文。这个加密方式是用于在登录时验证密码、注册时需要用到。
        我们可以自己选择一种加密方式,Spring security为我们提供了多种加密方式,我们这里使用一种强hash方式进行加密。
    加密方式.png
        在WebSecurityConfig 中注入(注入即可,不用声明使用),这样就会对提交的密码进行加密处理了,如果你没有注入加密方式,运行的时候会报错"There is no PasswordEncoder mapped for the id"错误。

        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            // 设置默认的加密方式(强hash方式加密)
            return new BCryptPasswordEncoder();
        }
    

        同样的我们数据库里存储的密码也要用同样的加密方式存储,例如我们将123456用BCryptPasswordEncoder 加密后存储到数据库中(注意:即使是同一个明文用这种加密方式加密出来的密文也是不同的,这就是这种加密方式的特点)
    image.png

    六、屏蔽Spring Security默认重定向登录页面以实现前后端分离功能

        在演示登录之前我们先编写一个查询接口"/getUser",并将"/getUser"接口规定为需要拥有"query_user"权限的用户可以访问,并在角色-权限关联关系表中给user1用户所属角色(role_id = 1)添加权限"query_user"
    image.png

        然后规定接口"/getUser"只能是拥有"query_user"权限的用户可以访问。后面我们基本都用这个查询接口作为演示,就叫它"资源接口"吧。

    http.authorizeRequests().
           antMatchers("/getUser").hasAuthority("query_user").
    

        演示登录时,如果用户没有登录去请求资源接口就会提示未登录
        在前后端不分离的时候当用户未登录去访问资源时Spring security会重定向到默认的登录页面,返回的是一串html标签,这一串html标签其实就是登录页面的提交表单。如图所示
    image.png
        而在前后端分离的情况下(比如前台使用VUE或JQ等)我们需要的是在前台接收到"用户未登录"的提示信息,所以我们接下来要做的就是屏蔽重定向的登录页面,并返回统一的json格式的返回体。而实现这一功能的核心就是实现AuthenticationEntryPoint并在WebSecurityConfig中注入,然后在configure(HttpSecurity http)方法中。AuthenticationEntryPoint主要是用来处理匿名用户访问无权限资源时的异常(即未登录,或者登录状态过期失效

    /**
     * @Author: Hutengfei
     * @Description: 匿名用户访问无权限资源时的异常
     * @Date Create in 2019/9/3 21:35
     */
    @Component
    public class CustomizeAuthenticationEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            JsonResult result = ResultTool.fail(ResultCode.USER_NOT_LOGIN);
            httpServletResponse.setContentType("text/json;charset=utf-8");
            httpServletResponse.getWriter().write(JSON.toJSONString(result));
        }
    }
    

    在WebSecurityConfig中的configure(HttpSecurity http)方法中声明

     //异常处理(权限拒绝、登录失效等)
     and().exceptionHandling().
     authenticationEntryPoint(authenticationEntryPoint).//匿名用户访问无权限资源时的异常处理
    

    再次请求资源接口
    image.png
    前台拿到这个错误时就可以做一些处理了,主要是退出到登录页面。

    1、实现登录成功/失败、登出处理逻辑

        首先需要明白一件事,对于登入登出我们都不需要自己编写controller接口,Spring Security为我们封装好了。默认登入路径:/login,登出路径:/logout。当然我们可以也修改默认的名字。登录成功失败和登出的后续处理逻辑如何编写会在后面慢慢解释。
        当登录成功或登录失败都需要返回统一的json返回体给前台,前台才能知道对应的做什么处理。
    而实现登录成功和失败的异常处理需要分别实现AuthenticationSuccessHandler和AuthenticationFailureHandler接口并在WebSecurityConfig中注入,然后在configure(HttpSecurity http)方法中然后声明

    (1)登录成功

    /**
     * @Author: Hutengfei
     * @Description: 登录成功处理逻辑
     * @Date Create in 2019/9/3 15:52
     */
    @Component
    public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        @Autowired
        SysUserService sysUserService;
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            //更新用户表上次登录时间、更新人、更新时间等字段
            User userDetails = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            SysUser sysUser = sysUserService.selectByName(userDetails.getUsername());
            sysUser.setLastLoginTime(new Date());
            sysUser.setUpdateTime(new Date());
            sysUser.setUpdateUser(sysUser.getId());
            sysUserService.update(sysUser);
            
            //此处还可以进行一些处理,比如登录成功之后可能需要返回给前台当前用户有哪些菜单权限,
            //进而前台动态的控制菜单的显示等,具体根据自己的业务需求进行扩展
    
            //返回json数据
            JsonResult result = ResultTool.success();
           //处理编码方式,防止中文乱码的情况
            httpServletResponse.setContentType("text/json;charset=utf-8");
           //塞到HttpServletResponse中返回给前台
            httpServletResponse.getWriter().write(JSON.toJSONString(result));
        }
    }
    

    (2)登录失败

    登录失败处理器主要用来对登录失败的场景(密码错误、账号锁定等…)做统一处理并返回给前台统一的json返回体。还记得我们创建用户表的时候创建了账号过期、密码过期、账号锁定之类的字段吗,这里就可以派上用场了.

    /**
     * @Author: Hutengfei
     * @Description: 登录失败处理逻辑
     * @Date Create in 2019/9/3 15:52
     */
    @Component
    public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            //返回json数据
            JsonResult result = null;
            if (e instanceof AccountExpiredException) {
                //账号过期
                result = ResultTool.fail(ResultCode.USER_ACCOUNT_EXPIRED);
            } else if (e instanceof BadCredentialsException) {
                //密码错误
                result = ResultTool.fail(ResultCode.USER_CREDENTIALS_ERROR);
            } else if (e instanceof CredentialsExpiredException) {
                //密码过期
                result = ResultTool.fail(ResultCode.USER_CREDENTIALS_EXPIRED);
            } else if (e instanceof DisabledException) {
                //账号不可用
                result = ResultTool.fail(ResultCode.USER_ACCOUNT_DISABLE);
            } else if (e instanceof LockedException) {
                //账号锁定
                result = ResultTool.fail(ResultCode.USER_ACCOUNT_LOCKED);
            } else if (e instanceof InternalAuthenticationServiceException) {
                //用户不存在
                result = ResultTool.fail(ResultCode.USER_ACCOUNT_NOT_EXIST);
            }else{
                //其他错误
                result = ResultTool.fail(ResultCode.COMMON_FAIL);
            }
           //处理编码方式,防止中文乱码的情况
            httpServletResponse.setContentType("text/json;charset=utf-8");
           //塞到HttpServletResponse中返回给前台
            httpServletResponse.getWriter().write(JSON.toJSONString(result));
        }
    }
    

    (3)登出

    同样的登出也要将登出成功时结果返回给前台,并且登出之后进行将cookie失效或删除

    /**
     * @Author: Hutengfei
     * @Description: 登出成功处理逻辑
     * @Date Create in 2019/9/4 10:17
     */
    @Component
    public class CustomizeLogoutSuccessHandler implements LogoutSuccessHandler {
        @Override
        public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            JsonResult result = ResultTool.success();
            httpServletResponse.setContentType("text/json;charset=utf-8");
            httpServletResponse.getWriter().write(JSON.toJSONString(result));
        }
    }
    

    2、在WebSecurityConfig中的configure(HttpSecurity http)方法中声明

    //登入
    and().formLogin().
    	permitAll().//允许所有用户
    	successHandler(authenticationSuccessHandler).//登录成功处理逻辑
    	failureHandler(authenticationFailureHandler).//登录失败处理逻辑
    
    //登出
    and().logout().
    	permitAll().//允许所有用户
    	logoutSuccessHandler(logoutSuccessHandler).//登出成功处理逻辑
    	deleteCookies("JSESSIONID").//登出之后删除cookie
    

    效果如图:
    登录时密码错误
    image.png
    登录时账号被锁定
    image.png
    退出登录之后再次请求资源接口
    image.png

    八、会话管理(登录过时、限制单用户或多用户登录等)

    1、限制登录用户数量

    比如限制同一账号只能一个用户使用

    and().sessionManagement().
                        maximumSessions(1)
    

    2、处理账号被挤下线处理逻辑

    同样的,当账号异地登录导致被挤下线时也要返回给前端json格式的数据,比如提示"账号下线"、"您的账号在异地登录,是否是您自己操作"或者"您的账号在异地登录,可能由于密码泄露,建议修改密码"等。这时就要实现SessionInformationExpiredStrategy(会话信息过期策略)来自定义会话过期时的处理逻辑。

    /**
     * @Author: Hutengfei
     * @Description: 会话信息过期策略
     * @Date Create in 2019/9/4 9:34
     */
    @Component
    public class CustomizeSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
        @Override
        public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) throws IOException, ServletException {
            JsonResult result = ResultTool.fail(ResultCode.USER_ACCOUNT_USE_BY_OTHERS);
            HttpServletResponse httpServletResponse = sessionInformationExpiredEvent.getResponse();
            httpServletResponse.setContentType("text/json;charset=utf-8");
            httpServletResponse.getWriter().write(JSON.toJSONString(result));
        }
    }
    

    3、在WebSecurityConfig中声明

    然后需要在WebSecurityConfig中注入,并在configure(HttpSecurity http)方法中然后声明,在配置同时登录用户数的配置下面再加一行 expiredSessionStrategy(sessionInformationExpiredStrategy)

    //会话管理
    and().sessionManagement().
    	maximumSessions(1).//同一账号同时登录最大用户数
    	expiredSessionStrategy(sessionInformationExpiredStrategy);//会话信息过期策略会话信息过期策略(账号被挤下线)
    

    效果演示步骤
    我电脑上用postman登录
    我电脑上请求资源接口,可以请求,如下左图
    在旁边电脑上再登录一次刚刚的账号
    在我电脑上再次请求资源接口,提示"账号下线",如右下图

    image.png

    九、实现基于JDBC的动态权限控制

    在之前的章节中我们配置了一个

    antMatchers("/getUser").hasAuthority("query_user")
    

        其实我们就已经实现了一个所谓的基于RBAC的权限控制,只不过我们是在WebSecurityConfig中写死的,但是在平时开发中,难道我们每增加一个需要访问权限控制的资源我们都要修改一下WebSecurityConfig增加一个antMatchers(…)吗,肯定是不合理的。因此我们现在要做的就是将需要权限控制的资源配到数据库中,当然也可以存储在其他地方,比如用一个枚举,只是我觉得存在数据库中更加灵活一点。
        我们需要实现一个AccessDecisionManager(访问决策管理器),在里面我们对当前请求的资源进行权限判断,判断当前登录用户是否拥有该权限,如果有就放行,如果没有就抛出一个"权限不足"的异常。不过在实现AccessDecisionManager之前我们还需要做一件事,那就是拦截到当前的请求,并根据请求路径从数据库中查出当前资源路径需要哪些权限才能访问,然后将查出的需要的权限列表交给AccessDecisionManager去处理后续逻辑。那就是需要先实现一个SecurityMetadataSource,翻译过来是"安全元数据源",我们这里使用他的一个子类FilterInvocationSecurityMetadataSource。
        在自定义的SecurityMetadataSource编写好之后,我们还要编写一个拦截器,增加到Spring security默认的拦截器链中,以达到拦截的目的。
        同样的最后需要在WebSecurityConfig中注入,并在configure(HttpSecurity http)方法中然后声明

    1、权限拦截器

    /**
     * @Author: Hutengfei
     * @Description: 权限拦截器
     * @Date Create in 2019/9/4 16:25
     */
    @Service
    public class CustomizeAbstractSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    
        @Autowired
        private FilterInvocationSecurityMetadataSource securityMetadataSource;
    
        @Autowired
        public void setMyAccessDecisionManager(CustomizeAccessDecisionManager accessDecisionManager) {
            super.setAccessDecisionManager(accessDecisionManager);
        }
    
        @Override
        public Class<?> getSecureObjectClass() {
            return FilterInvocation.class;
        }
    
        @Override
        public SecurityMetadataSource obtainSecurityMetadataSource() {
            return this.securityMetadataSource;
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
            invoke(fi);
        }
    
        public void invoke(FilterInvocation fi) throws IOException, ServletException {
            //fi里面有一个被拦截的url
            //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
            //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
            InterceptorStatusToken token = super.beforeInvocation(fi);
            try {
            //执行下一个拦截器
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.afterInvocation(token, null);
            }
        }
    }
    

    2、安全元数据源FilterInvocationSecurityMetadataSource

    /**
     * @Author: Hutengfei
     * @Description:
     * @Date Create in 2019/9/3 21:06
     */
    @Component
    public class CustomizeFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        @Autowired
        SysPermissionService sysPermissionService;
        @Override
        public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
            //获取请求地址
            String requestUrl = ((FilterInvocation) o).getRequestUrl();
            //查询具体某个接口的权限
            List<SysPermission> permissionList =  sysPermissionService.selectListByPath(requestUrl);
            if(permissionList == null || permissionList.size() == 0){
                //请求路径没有配置权限,表明该请求接口可以任意访问
                return null;
            }
            String[] attributes = new String[permissionList.size()];
            for(int i = 0;i<permissionList.size();i++){
                attributes[i] = permissionList.get(i).getPermissionCode();
            }
            return SecurityConfig.createList(attributes);
        }
    
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return true;
        }
    }
    

    3、访问决策管理器AccessDecisionManager

    /**
     * @Author: Hutengfei
     * @Description: 访问决策管理器
     * @Date Create in 2019/9/3 20:38
     */
    @Component
    public class CustomizeAccessDecisionManager implements AccessDecisionManager {
        @Override
        public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
            Iterator<ConfigAttribute> iterator = collection.iterator();
            while (iterator.hasNext()) {
                ConfigAttribute ca = iterator.next();
                //当前请求需要的权限
                String needRole = ca.getAttribute();
                //当前用户所具有的权限
                Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                for (GrantedAuthority authority : authorities) {
                    if (authority.getAuthority().equals(needRole)) {
                        return;
                    }
                }
            }
            throw new AccessDeniedException("权限不足!");
        }
    
        @Override
        public boolean supports(ConfigAttribute configAttribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> aClass) {
            return true;
        }
    }
    

    4、在WebSecurityConfig中声明

    先在WebSecurityConfig中注入,并在configure(HttpSecurity http)方法中然后声明

            http.authorizeRequests().
                    withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                        @Override
                        public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                            o.setAccessDecisionManager(accessDecisionManager);//访问决策管理器
                            o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
                            return o;
                        }
                    });
            http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);//增加到默认拦截链中
    

    十、最终的WebSecurityConfig配置

    /**
     * @Author: Hutengfei
     * @Description:
     * @Date Create in 2019/8/28 20:15
     */
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        //登录成功处理逻辑
        @Autowired
        CustomizeAuthenticationSuccessHandler authenticationSuccessHandler;
    
        //登录失败处理逻辑
        @Autowired
        CustomizeAuthenticationFailureHandler authenticationFailureHandler;
    
        //权限拒绝处理逻辑
        @Autowired
        CustomizeAccessDeniedHandler accessDeniedHandler;
    
        //匿名用户访问无权限资源时的异常
        @Autowired
        CustomizeAuthenticationEntryPoint authenticationEntryPoint;
    
        //会话失效(账号被挤下线)处理逻辑
        @Autowired
        CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;
    
        //登出成功处理逻辑
        @Autowired
        CustomizeLogoutSuccessHandler logoutSuccessHandler;
    
        //访问决策管理器
        @Autowired
        CustomizeAccessDecisionManager accessDecisionManager;
    
        //实现权限拦截
        @Autowired
        CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;
    
        @Autowired
        private CustomizeAbstractSecurityInterceptor securityInterceptor;
    
        @Bean
        public UserDetailsService userDetailsService() {
            //获取用户账号密码及权限信息
            return new UserDetailsServiceImpl();
        }
    
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            // 设置默认的加密方式(强hash方式加密)
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and().csrf().disable();
            http.authorizeRequests().
                    //antMatchers("/getUser").hasAuthority("query_user").
                    //antMatchers("/**").fullyAuthenticated().
                    withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                        @Override
                        public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                            o.setAccessDecisionManager(accessDecisionManager);//决策管理器
                            o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
                            return o;
                        }
                    }).
                    //登出
                    and().logout().
                        permitAll().//允许所有用户
                        logoutSuccessHandler(logoutSuccessHandler).//登出成功处理逻辑
                        deleteCookies("JSESSIONID").//登出之后删除cookie
                    //登入
                    and().formLogin().
                        permitAll().//允许所有用户
                        successHandler(authenticationSuccessHandler).//登录成功处理逻辑
                        failureHandler(authenticationFailureHandler).//登录失败处理逻辑
                    //异常处理(权限拒绝、登录失效等)
                    and().exceptionHandling().
                        accessDeniedHandler(accessDeniedHandler).//权限拒绝处理逻辑
                        authenticationEntryPoint(authenticationEntryPoint).//匿名用户访问无权限资源时的异常处理
                    //会话管理
                    and().sessionManagement().
                        maximumSessions(1).//同一账号同时登录最大用户数
                        expiredSessionStrategy(sessionInformationExpiredStrategy);//会话失效(账号被挤下线)处理逻辑
            http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
        }
    }
    

    十一、结束语

    到现在为止本文就基本结束了,在本文中我们利用Springboot+Spring security实现了前后端分离的用户登录认证和动态的权限访问控制。
    有疑问可以联系我的微信18061495586
    如果大家觉得本文对您有帮助可以给我一个小心心鼓励一下嘻嘻
    最后附上github地址:
    github

    展开全文
  • 菜单访问权限控制 配置helloword菜单【系统管理】-【菜单管理】 其中前端组件配置相对src/views/目录下的 目录名+文件名 例如页面src/views/jeecg/helloworld.vue 前端组件配置 jeecg/helloworld 用户角色授权...
  • Spring Security 中的四种权限控制方式

    万次阅读 多人点赞 2020-06-17 09:21:49
    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权...
  • Shiro权限控制(六):Shiro按钮权限控制

    千次阅读 2019-10-07 21:48:40
    一、前言 ...二、方案描述 后端返回用户的所有角色及权限,前端自定义权限标签,通过标签中指定的权限与后端返回的权限做对比,有权限则按钮显示,无权限则按钮隐藏 PS:权限控制不仅是前端按钮的按...
  • vue 全局权限控制

    千次阅读 2019-08-01 15:14:04
    权限控制一直是一个很烦人的事情,无论是前端还是后台。 在平常的项目里,可能大家都是通过后台传来的字段来控制元素的display属性。 其实这么做是很不严谨的。 因为可以通过F12查看元素,也可以进行更改,这个时候...
  • 前端权限控制

    万次阅读 多人点赞 2018-05-22 17:03:11
    为什么做前端权限控制前端权限控制并不是新生事物,早在后端 MVC 时代,web 系统中就已经普遍存在对按钮和菜单的显示 / 隐藏控制,只不过当时它们是由后端程序员在 jsp 或者 php 模板中实现的。随着前后端分离架构的...
  • MySQL权限控制

    千次阅读 2019-03-09 14:30:24
    身份认证模块,即MySQL客户端通过指定用户名,密码,主机名来连接MySQL服务器,主要是认证给定的用户是否有权限连接MySQL服务器,而权限控制模块主要用于控制已经通过身份认证的用户对数据库、数据库表、数据表的列...
  • C# winform 权限控制 包括角色 用户 权限设置,拿出来共享
  • Shiro权限控制+整合shiro

    万次阅读 多人点赞 2019-03-02 13:17:56
    Shiro权限控制 0.1传统的权限认证方式 特点:为每个人单独的分配权限模块,能够实现权限控制,但是当公司人员庞大之后,非常难管理 上述权限控制如何设计表? 关系:员工和菜单权限的关系:多对多 员工id 菜单...
  • JAVAWEB权限控制源码

    热门讨论 2013-08-02 11:50:09
    2.使用二进制进行权限控制 3.完整代码,有大部分注释 4.包括用户模块、部门、帐号管理、菜单模块、权限管理 5.里面有用到jquery.colorbox、JQuery zTree 等JS插件,友好的界面,整齐好看 6.三层架构、比较完整的底层...
  • Vue权限控制1.权限相关概念1.1.权限的分类后端权限前端权限1.2.前端权限的意义2.前端权限控制思路2.1.菜单的控制2.2.界⾯的控制2.3.按钮的控制2.4.请求和响应的控制3.Vue的权限控制实现3.1.菜单的控制查看登录之后...
  • 在进行业务软件开发的时候,都会涉及到权限控制的问题,asp.net core mvc提供了相关特性。 在具体介绍使用方法前,我们需要先了解几个概念: 1,claim:英文翻译过来是声明的意思,一个claim包含Type,Value两项信息...
  • Jeecg权限控制

    千次阅读 2016-08-04 17:59:18
    Jeecg权限控制一、Jeecg权限涉及的概念 序号 内容 说明 1、 用户 具体登录的用户 2、 角色 一个访问权限的集合 3、 组织机构 一个部门的概念 4、 数据规则 行数据的访问控制 二、说明 1、组织机构:...
  • SpringBoot权限控制

    千次阅读 2017-10-08 12:01:23
    权限控制是一个比较重要的知识点。 先讲一下相关理论知识,如图: 通过SpringBoot做权限控制的步骤如下: 1、首先要引入相应的包。 除了要引入其他基本功能的包,还要引入和权限控制相关的包,pom代码如下:<...
  • Nacos 权限控制介绍及实战

    千次阅读 2020-04-01 14:45:47
    Nacos自开源依赖,权限控制一直需求比较强烈,这也反应了用户需求将Nacos部署到生产环境的需求。最新发布的Nacos 1.2.0版本已经支持了服务发现和配置管理的权限控制,保障用户安全上生产。本文主要介绍Nacos权限控制...
  • 帆软报表权限控制

    千次阅读 2019-04-02 15:52:07
    FineReport报表通过权限控制数据访问方案 问题:实际应用环境中,不同角色的人可能对数据具有不同的访问权限,通过直接在SQL语句中筛选出需要的数据制作模板可以解决该需求,但对于角色较多的情况,就需要制作很多...
  • springboot整合shiro实现权限控制

    万次阅读 多人点赞 2019-05-24 12:27:40
    使用Shiro的易于理解的API,您可以...上个月写了一个在线教育的项目用到了shiro权限控制,这几天又复盘了一下,对其进行了深入探究,来总结一下。 下面所总结的有关shiro的代码已经传到我的github上,可以访问下面的...
  • 随着前后端分离架构的流行,权限控制这个曾经陌生的话题开始进入很多前端同学的视野,作为一名前端,可能部分同学会认为权限是后端的事,其实这个想法是片面的,后端是权限控制的最后一扇门,而在这之前,做好前端...
  • vue前端按钮权限控制隐藏

    万次阅读 多人点赞 2019-04-10 19:41:23
    vue前端按钮权限控制隐藏权限控制描述如何实现1、储存权限数据2、权限判断方法3、设置vue自定义指令4、最终代码与效果 很多后台管理系统会用到权限管理,菜单权限比较好控制,按钮权限就复杂一些,本文介绍的就是...
  • 权限控制的分类介绍

    千次阅读 2018-08-22 23:25:12
    权限控制分类:数据权限、访问权限 访问权限:访问权限从表面意思就可以看出是否有权限访问该功能,大部分访问权限通过菜单的URL来控制,通过用户关联不同的菜单来进行权限控制,这里主要有俩种实现方式,一是设置...
  • Spring Security权限控制

    千次阅读 2019-06-27 14:50:17
    引言 Spring Security是一个能够为基于Spring的企业应用系统提供安全访问控制解决方案的...Spring Security支持Url级别的权限控制,同样也支持方法级别的权限控制,今天主要介绍Spring Security方法级别的权限控...
  • hive权限控制

    千次阅读 2016-04-27 00:37:17
    查询hive中的数据时爆出了: ...是自己开启hive权限控制后,现在使用的用户权限不足。因此只需要将select权限赋予用户即可。Hive的权限控制并不是完全安全的。hive的权限控制是为了防止用户不小心做了不合适的操作
  • 如何配置Jira项目权限方案? 能控制不同成员访问不同单据和字段吗... 本课程介绍权限控制的具体实施方案,针对项目、单据、字段,演示三种不同层级粒度权限的配置、扩展和定制方法,提供脚本代码,应用于实际业务场景。
  • 现在开发中出现一个问题,权限值保存在session中来实现对页面进行权限控制, 会出现强转失败,所以想问一下:一般开发中当管理员和普通用户需要使用同一套页面的时候使用权限控制合适还是使页面分离合适
  • Java学习-访问权限控制

    千次阅读 2020-08-08 23:12:38
    Java学习-访问权限控制 1、为什么要有访问权限控制? 2、包:库单元 3、Java访问权限修饰词 1、为什么要有访问权限控制? 访问控制(或隐藏具体实现)与“最初的实现并不恰当”有关。 所有优秀的作者,包括那些...
  • Hive访问权限控制

    千次阅读 2018-07-18 16:03:52
    hive有两种类型的权限控制方式: 一。通过Hcatcalog API访问hive数据的方式,实际是通过访问metastore元数据的形式访问hive数据,这类有MapReduce,impala,pig,Spark SQL,hive Command line等方式,基于这种方式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 829,228
精华内容 331,691
关键字:

权限控制