自学内容网 自学内容网

Spring Security授权案例集合

授权

前置内容

描述:授权是指认证通过后,系统赋予用户一定的权限,而用户能根据此权限,访问系统中的某些资源
授权方式:RBAC
基于角色的访问控制(一般不用)
直接给某个角色赋予访问权限

角色A有权利访问资源a,但当A发生变化时,则需要进行修改

基于资源的访问控制(常用)
根据角色拥有的资源权限,赋予额外的资源权限

角色A有权利访问资源a
+
访问资源a的所有角色有权利访问资源b
---->>
角色A可以访问资源b

权限表的设计

用户与权限的关系:多对多
原则设计:设计中间表,降低范式化程度
用户、角色、权限
如果只有用户和权限两表,操作量会非常大,因此增加角色表和两个中间表,保证低范式。
在这里插入图片描述

建表和相关说明

表说明
权限
在这里插入图片描述
角色
在这里插入图片描述
角色权限关系表
在这里插入图片描述
用户
在这里插入图片描述
用户角色关系表
在这里插入图片描述

建表代码

CREATE TABLE `users` (
 `uid` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `users` VALUES (1, 'baizhan', '$2a$10$Eqv9PRMl6bPt5BiwgPr2eucgyl.E.xLENt4bvfDvv7DyS5AVPT.U6', '13812345678');


CREATE TABLE `role` (
 `rid` int(11) NOT NULL AUTO_INCREMENT,
 `roleName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `roleDesc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role` VALUES (1, '总经理', '管理整个公司');
INSERT INTO `role` VALUES (2, '股东', '参与公司决策');
INSERT INTO `role` VALUES (3, '财务', '管理公司资产');


CREATE TABLE `permission` (
 `pid` int(11) NOT NULL AUTO_INCREMENT,
 `permissionName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
 PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `permission` VALUES (1, '查询报表', '/reportform/find');
INSERT INTO `permission` VALUES (2, '查询工资', '/salary/find');
INSERT INTO `permission` VALUES (3, '查询税务', '/tax/find');


CREATE TABLE `users_role` (
 `uid` int(255) NOT NULL,
 `rid` int(11) NOT NULL,
 PRIMARY KEY (`uid`, `rid`) USING BTREE,
 INDEX `rid`(`rid`) USING BTREE,
 CONSTRAINT `users_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `users_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `users_role` VALUES (1, 2);
INSERT INTO `users_role` VALUES (1, 3);


CREATE TABLE `role_permission` (
 `rid` int(11) NOT NULL,
 `pid` int(11) NOT NULL,
 PRIMARY KEY (`rid`, `pid`) USING BTREE,
 INDEX `pid`(`pid`) USING BTREE,
 CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `permission` (`pid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (3, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (2, 3);

用户权限配置

内部关键类GrantedAuthority和SimpleGrantedAuthority

描述:GrantedAuthority是security内部中的权限接口,SimpleGrantedAuthority是前者的一个简单实现。
使用说明
创建GrantedAuthority列表存储所有的用户权限,存入的是SimpleGrantedAuthority的实例,SimpleGrantedAuthority每个实例在创建时,需要写入权限信息(String类型),写入方式一般为自己创建的权限实体类

关键类使用示例
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (Permission permission : permissions) {
    grantedAuthorities.add(new SimpleGrantedAuthority(permission.getUrl()));
}

用户权限配置完整代码
此类的作用是负责处理用户、权限和数据库部分的交互逻辑
内部只定义了一个方法,用于根据给定用户名,加载用户信息(包含权限内容),用于进行授权
根据用户名加载一个用户时,会从数据库中读取这个用户的相应权限和其他信息,同时这些信息被写入到这个用户实例上,完成了授权操作

package com.wunaiieq.tmp2024121105.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wunaiieq.tmp2024121105.entity.Permission;
import com.wunaiieq.tmp2024121105.entity.Users;
import com.wunaiieq.tmp2024121105.mapper.UsersMapper;
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.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private UsersMapper usersMapper;
    //自定义认证逻辑
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.查询条件
        QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);
        //2.查询用户
        Users users = usersMapper.selectOne(wrapper);
        if (users == null){
            return null;
        }
        //3.查询用户权限,用我们自定义的Permission类去接收,捕获成一个列表
        List<Permission> permissions = usersMapper.findPermissionByUsername(username);
        //4.将自定义的权限集合转换为security规定的权限集合
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (Permission permission : permissions) {
            grantedAuthorities.add(//将实例添加到grantedAuthorities集合中
                    new SimpleGrantedAuthority(//将下面获取到的权限(字符串形式)创建出SimpleGrantedAuthority的示例
                            permission.getUrl()//调用列表中每个Permission对象的getUrl()方法,获取权限
                    )
            );
        }
        //5.封装为UserDetails对象
        UserDetails userDetails = User.withUsername(users.getUsername())
                .password(users.getPassword())
                .authorities(grantedAuthorities)
                .build();
        //4.返回封装好的UserDetails对象
        return userDetails;
    }
}

资源访问控制

方法一、配置类设置访问控制

controller
controller层中设置对应的url,作为资源

 @GetMapping("/reportform/find")
        public String findReportForm() {
            return "查询报表";
        }


        @GetMapping("/salary/find")
        public String findSalary() {
            return "查询工资";
        }


        @GetMapping("/staff/find")
        public String findStaff() {
            return "查询员工";
        }

示例
在配置类中修改如下

        httpSecurity.authorizeHttpRequests(
                resp -> {
                    resp.requestMatchers(new AntPathRequestMatcher("/login.html"), new AntPathRequestMatcher("/fail,html")).permitAll(); // 不需要认证的资源
                    resp.requestMatchers(new AntPathRequestMatcher("/staff/find")).hasAnyAuthority("/staff/find");
                    resp.anyRequest().authenticated();//其余所有请求都需要认证
                }   
        );

以资源去匹配权限
资源过多时,用此方法,在访问资源时,判断用户是否具有访问此url的权限
在SecurityConfigure中进行修改

        httpSecurity.authorizeHttpRequests(resp -> {
            resp.requestMatchers(new AntPathRequestMatcher("/login.html"), new AntPathRequestMatcher("/fail.html")).permitAll(); // 不需要认证的资源
            resp.requestMatchers(new AntPathRequestMatcher("/css/*.css"), new AntPathRequestMatcher("/js/*.js"), new AntPathRequestMatcher("/img/**")).permitAll(); // 静态资源不需要认证
            /**
             * access的参数是一个函数式接口
             * 方法的第一个参数代表认证对象,可以获取认证用户的权限集合
             * 方法的第二个参数代表网络环境,可以获取当前请求的路径
             */
            resp.anyRequest().access(
                    (authentication, requestContext) -> {
                        // 获取认证的用户权限
                        Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                        // 获取请求的URL路径
                        String uri = requestContext.getRequest().getRequestURI();
                        // 将URL路径封装为权限对象
                        SimpleGrantedAuthority authority = new SimpleGrantedAuthority(uri);
                        // 判断用户的权限集合是否包含请求的URL权限对象
                        boolean result = authorities.contains(authority);
                        return new AuthorizationDecision(result);
                    }
                );
            }
        );

方法二、注解类设置访问控制(优选)

1. 需要在启动类中开启注解:@EnableMethodSecurity

package com.wunaiieq.tmp2024121105;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@SpringBootApplication
@MapperScan("com.wunaiieq.tmp2024121105.mapper")
@EnableMethodSecurity
public class Tmp2024121105Application {

    public static void main(String[] args) {
        SpringApplication.run(Tmp2024121105Application.class, args);
    }

}

2. controller对应的方法中增加注解

        @GetMapping("/staff/find")
        @PreAuthorize("hasAnyAuthority('/staff/find')")
        public String findStaff() {
            return "查询员工";
        }

权限不足的处理方法

编写全写不足处理类,在权限不足时跳转指定页面,或弹窗等
权限不足的处理类

package com.wunaiieq.tmp2024121105.handler;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.sendRedirect("/noPermission.html");
    }
}

配置文件中增加处理权限不足的异常

httpSecurity.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());

原文地址:https://blog.csdn.net/wusuoweiieq/article/details/144429689

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!