SpringSecurity原理和实际应用

news2024/11/29 9:37:55

前提知识

认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

实现认证和授权需要以下七张表

所有用户都存在t_user中

给用户授予不同的权限,权限保存在t_premission中

 角色表t_role是便于为用户授权而建立的表,角色就是一堆权限的集合,角色和权限是多对多的关系

页面的全部菜单保存在t_menu中

 以上四个主表通过三张副表实现两两对应关系,且都是多对多关系

用户user和角色role进行关联

角色role和premission关联

角色role和菜单menu关联

角色表有着重要作用,因为都和角色表有多对多关系

用户表ID和角色表ID相关联,关联信息在user_role表中

用户需要不同角色,赋予不同的角色ID

认证过程:只需要用户表就可以了,在用户登录时可以查询用户表t_user进行校验,判断用户输入的用户名和密码是否正确。

授权过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

Spring Security可以帮助我们来简化认证和授权

项目中应用

1.在web.xml中配置文件

<!--/*表示安全框架启动之后会扫描所有的请求-->
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>

2.resources提供spring_security配置文件

1.设置拦截规则 

<security:http auto-config="true" use-expressions="true">

    <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />

2.配置认证管理器

<!--认证管理器,用于处理认证操作-->
    <security:authentication-manager>
        <!--认证提供者,执行具体的认证逻辑,此处爆红不影响使用-->
        <security:authentication-provider user-service-ref="springSecurityUserService">
            <!--指定密码加密策略-->
            <security:password-encoder ref="passwordEncoder" />
        </security:authentication-provider>
    </security:authentication-manager>

{noop}表示明文

 项目启动之后

web.xml

<!--/*表示安全框架启动之后会扫描所有的请求-->
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>

spring_security配置文件

<!--
            intercept-url:定义一个拦截规则
            pattern:对哪些url进行权限控制
            access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,请求的用户只需拥有其中的一个角色就能成功访问对应的URL
            isAuthenticated():已经经过认证(不是匿名用户),只要是
        -->
        <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />

配置可以匿名访问的资源 

 <!--
        http:用于定义相关权限控制
        指定哪些资源不需要进行权限校验,匿名(不登陆)即可访问以下资源,可以使用通配符
    -->
    <security:http security="none" pattern="/js/**" />
    <security:http security="none" pattern="/css/**" />
    <security:http security="none" pattern="/img/**" />
    <security:http security="none" pattern="/plugins/**" />

使用自己指定的页面作为登录页面

username,password对应登录界面的参数

login-processing-url:登录表单提交的地址,后续不需要创建controller类,框架会自动处理 default-target-url:认证成功跳转界面

authentication-failure-url:失败跳转界面

<!--form-login:定义表单登录信息-->
        <security:form-login login-page="/login.html"
                             username-parameter="username"
                             password-parameter="password"
                             login-processing-url="/login.do"
                             default-target-url="/pages/main.html"
                             always-use-default-target="true"
                             authentication-failure-url="/login.html"
        />

配置crsf项

<!--
            csrf:对应CsrfFilter过滤器
            disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403)。默认是开启的,true表示关闭
        -->
        <security:csrf disabled="true"></security:csrf>

实现从数据库中查询用户

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个实现UserDetailsService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。

1.创建一个service类

/**
 * 动态给用户授予角色和权限
 */
@Component
public class SpringSecurityUserService implements UserDetailsService {

    @Reference //注意:此处要通过dubbo远程调用用户服务
    private UserService userService;

    //根据用户名查询用户信息
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            //用户名不存在
            return null;
        }
        //动态为当前用户授权
        List<GrantedAuthority> list = new ArrayList<>();
        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            //遍历角色,为用户授予角色
            list.add(new SimpleGrantedAuthority(role.getKeyword()));
            Set<Permission> permissions = role.getPermissions();
            for (Permission permission : permissions) {
                //遍历权限集合,为用户授权
                list.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }
        org.springframework.security.core.userdetails.User securityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(),list);
        return securityUser;
    }
}
//User是框架提供的User,根据用户名查询数据库信息(包含存储的密码,将用户信息返回给框架,框架会进行密码比对
User user = userService.findByUsername(username);

密码加密

bcrypt:将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题

第一步:在spring-security.xml文件中指定密码加密对象

user1.setPassword(passwordEncoder.encode("admin"));

数据库中的密码是经过加密之后的密码,前端提交的密码会自动加密并和数据库中作比较

记得在xml文件中开启注解控制

配置多种校验规则

<!--只要认证通过就可以访问-->
<security:intercept-url pattern="/index.jsp"  access="isAuthenticated()" />
<security:intercept-url pattern="/a.html"  access="isAuthenticated()" />

<!--拥有add权限就可以访问b.html页面-->
<security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />

<!--拥有ROLE_ADMIN角色就可以访问c.html页面-->
<security:intercept-url pattern="/c.html"  access="hasRole('ROLE_ADMIN')" />

<!--拥有ROLE_ADMIN角色就可以访问d.html页面,
	注意:此处虽然写的是ADMIN角色,框架会自动加上前缀ROLE_-->
<security:intercept-url pattern="/d.html"  access="hasRole('ADMIN')" />

注解方式权限控制

针对不同页面访问的权限要求,SpringSecurity提供了注解方式权限控制,可以精确到方法控制级别

第一步:在spring-security.xml文件中配置组件扫描,用于扫描Controller

<mvc:annotation-driven></mvc:annotation-driven>
<context:component-scan base-package="com.itheima.controller"></context:component-scan>

第二步:在spring-security.xml文件中开启权限注解支持

<!--开启注解方式权限控制-->
<security:global-method-security pre-post-annotations="enabled" />

controller类中的代码

@RestController
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/add")
    @PreAuthorize("hasAuthority('add')")//表示用户必须拥有add权限才能调用当前方法
    public String add(){
        System.out.println("add...");
        return "success";
    }

    @RequestMapping("/delete")
    @PreAuthorize("hasRole('ROLE_ADMIN')")//表示用户必须拥有ROLE_ADMIN角色才能调用当前方法
    public String delete(){
        System.out.println("delete...");
        return "success";
    }
}

注意注解代码中的权限必须和数据中权限表的关键字一致

七牛云绑定自定义域名

1.新建存储空间,并使用javaSDK方式操作空间

Java SDK的所有的功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access Key和Secret Key。

后端代码

新建上传文件工具类Utils。工具类中需要配置云存储的相关参数,

public class QiniuUtils {
    public  static String accessKey = "YLRQlxIU5IyLEGSbhFt-hAYKvGY0_zNk_eABhM8t";
    public  static String secretKey = "eswtzJogA9qDSne9Z9uZM8Kt8QNS0AYPNS971dxX";
    public  static String bucket = "ahtcm-space-01";

    //上传文件(方法1
    public static void upload2Qiniu(String filePath,String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Region.region2());//使用华南机房
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(filePath, fileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println("文件名:" + putRet.key);
            System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }
    }

    //上传文件(法2
    public static void upload2Qiniu(byte[] bytes, String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Region.region2());//使用华南机房
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);

        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = fileName;
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(bytes, key, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println("文件名:" + putRet.key);
            System.out.println("文件在云存储中的唯一标识符:" + putRet.hash);
        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }
    }

    //删除文件
    public static void deleteFileFromQiniu(String fileName){
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone0());
        String key = fileName;
        Auth auth = Auth.create(accessKey, secretKey);
        BucketManager bucketManager = new BucketManager(auth, cfg);
        try {
            bucketManager.delete(bucket, key);
        } catch (QiniuException ex) {
            //如果遇到异常,说明删除失败
            System.err.println(ex.code());
            System.err.println(ex.response.toString());
        }
    }
}

在controller中调用工具类方法进行文件上传

//文件上传,图片上传
    @RequestMapping("/upload")
    public Result upload(@RequestParam("imgFile") MultipartFile imgFile) {//使用springmvc的上传组件来上传文件,后台接收到文件
        //System.out.println(imgFile);
        //获取原始文件名
        String originalFilename = imgFile.getOriginalFilename();
        int lastIndexOf = originalFilename.lastIndexOf(".");
        //获取文件后缀
        String suffix = originalFilename.substring(lastIndexOf - 1);
        //使用UUID随机产生文件名称,防止同名文件覆盖
        String fileName = UUID.randomUUID().toString() + suffix;
        try {
            QiniuUtils.upload2Qiniu(imgFile.getBytes(), fileName);//使用七牛云工具类的bytes数组上传文件方法,第二个参数为文件名
            //将上传图片名称存入Redis,基于Redis的Set集合存储
            String urlFileName = "http://ruh9fwjil.hn-bkt.clouddn.com/" + fileName;
            jedisPool.getResource().sadd(RedisConstant.RECORD_PIC_RESOURCES,urlFileName);
            //图片上传成功
            Result result = new Result(true, MessageConstant.PIC_UPLOAD_SUCCESS);
            result.setData(fileName);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            //图片上传失败
            return new Result(false, MessageConstant.PIC_UPLOAD_FAIL);
        }
    }

如果网络地址配置正确,但前端不能实时查看到文件,去云平台查看是否是将空间设置为了私有空间,私有空间中的bucket不能被直接访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/528274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Resource接口 学习

Resource 接口是 Spring 资源访问策略的抽象&#xff0c;它本身并不提供任何资源访问实现&#xff0c;具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。Resource一般包括这些实现类&#xff1a;UrlResource、ClassPathResource、FileSystemResource、S…

MySQL基础(三十二)事务基础知识

1 数据库事务概述 1.1 存储引擎支持情况 SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些&#xff0c;以及这些存储引擎是否支持事务。 能看出在 MySQL 中&#xff0c;只有InnoDB 是支持事务的。 1.2 基本概念 事务&#xff1a;一组逻辑操作单元&#xff0c;使数…

由浅入深理解java集合(三)——集合 List

一、List集合 List集合判断元素相等的标准 List判断两个对象相等只要通过equals()方法比较返回true即可&#xff08;关于equals()方法的详解可以参考第二篇文章中的内容&#xff09;。 下面以用代码具体展示。 创建一个Book类&#xff0c;并重写equals()方法&#xff0c;如果两…

ctf.show MiSC入门 图片篇 (信息附加)

目录 图片篇 信息附加 misc5 misc6 misc7 misc8 misc9 misc10 misc11 misc12 misc13 misc14 misc15 misc16 misc17 misc18 misc19 misc20 misc 21 misc22 misc23 misc41 图片篇 信息附加 misc5 打开后啥也没有 使用16进制编辑器打开&#xff0c;在最下面…

就业内推 | 上市公司招网工运维,有华为、思科、华三认证均可

01 软通动力 &#x1f537;招聘岗位&#xff1a;网络工程师 &#x1f537;职责描述&#xff1a; 1、负责大型数据中心网络运维及变更&#xff0c;包括架构优化、性能调优、服务上线 2、负责网络故障类问题定位及排查&#xff0c;对于复杂故障类问题能够定位、跟进和解决实施等…

数据分析案例-BI工程师招聘岗位信息可视化分析(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

25.在vue中使用axios

目录 1 项目中安装 axios 2 做测试用的后端 3 全局引入axios 4 定义全局根路径 1 项目中安装 axios 2 做测试用的后端 我们用python的flask做后端 简单来讲就是&#xff0c;发两个数字&#xff0c;如果是get就两个数相加&#xff0c;如果是post就两个数相乘 3 全局引…

Kali-linux使用Nessus

Nessus号称是世界上最流行的漏洞扫描程序&#xff0c;全世界有超过75000个组织在使用它。该工具提供完整的电脑漏洞扫描服务&#xff0c;并随时更新其漏洞数据库。Nessus不同于传统的漏洞扫描软件&#xff0c;Nessus可同时在本机或远端上遥控&#xff0c;进行系统的漏洞分析扫描…

【数据结构初阶】——第八节.优先级队列(小根堆的模拟实现)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;Java初阶数据结构 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 目录 …

API接口三问

一、API数据接口可以给我们带来哪些便利 API数据接口可以给我们带来以下便利&#xff1a; 数据共享&#xff1a;API允许数据在不同的应用程序之间共享。这使得数据转移更容易&#xff0c;因为不需要手动复制和粘贴数据内容。 程序集成&#xff1a;API作为中间件&#xff0c;可…

20年+资深审稿人:什么情况下建议文章大小修、拒稿或接收?

文章进入外审后&#xff0c;作者最终可能会得到大小修、接收或拒稿的意见。那么&#xff0c;审稿人是怎么给出这些不同意见的呢&#xff1f;有哪些方面需要作者提前了解呢&#xff1f; Surgery 发布过一篇文章&#xff0c;里面调查了一些具有20年审稿经验、平均年龄69岁的编委会…

ThreadLocal八股文

目录 1. 为什么要⽤ ThreadLocal? 2. ThreadLocal 的原理是什么&#xff1f; 3. 为什么⽤ ThreadLocal 做 key&#xff1f; 4. Entry 的 key 为什么设计成弱引⽤&#xff1f; 5. ThreadLocal 真的会导致内存泄露&#xff1f; 6. 如何解决内存泄露问题&#xff1f; 7. T…

1M分辨率 中国各城市绿地数据的获取

城市绿地系统是城市总体规划的有机组成部分&#xff0c;反映了城市的自然属性。在人类选址建造城市之初&#xff0c;大多将城市选择在和山、川、江、湖相毗邻的地方&#xff0c;它给予城市的形态、功能布局及城市景观以很大影响。先有自然&#xff0c;后有城市&#xff0c;自然…

时间序列预测(三)基于Prophet+XGBoost的销售额预测

时间序列预测&#xff08;三&#xff09;基于ProphetXGBoost的销售额预测 前面我们介绍了如何使用Prophet和LSTM&#xff0c;不知道你们发现了没有&#xff0c;前者似乎太简单了&#xff0c;后者呢好像又很复杂。那有没有什么很好的方法能很好的中和下呢&#xff1f; 已知的有…

入门黑客(网络安全)需要准备什么?

之所以写这篇文章呢&#xff0c;是觉得大时代的发展&#xff0c;我们这个专业越来越受到重视了&#xff0c;所以&#xff0c;也想以自己的一些拙见&#xff0c;能帮到想入门网络安全的朋友 1.关于网上的培训 如果你想快速获得知识&#xff0c;培训无疑是最快最有效的捷径&…

ANR概述

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 系列文章目录前言一、ANR是什么&#xff1f;二、ANR超时阈值三、前后与台区别1.前台与后台服务的区…

核心案例 | 南京理工大学空地协同编队控制系统建设项目

项目名称&#xff1a;空地协同编队控制系统建设项目 场 地&#xff1a;室内/室外 关 键 词&#xff1a;自主导航与SLAM、集群协同决策、集群控制 南京理工大学核心案例(1) 01 项目背景 本项目通过集群四旋翼无人机、天地协同集群控制开发环境、无人机协同集群控制系统…

音频转换成mp3的方法

把音频转换为MP3格式是因为MP3格式可以更好地压缩音频文件&#xff0c;减小文件大小&#xff0c;便于存储和传输。此外&#xff0c;MP3格式已成为流行的音频格式之一&#xff0c;许多设备和软件都支持MP3格式&#xff0c;使得MP3格式成为了一种通用的音频格式。总的来说&#x…

log4Qt史上最详细介绍、编译和使用

文章目录 Log4Qt介绍下载log4qt源码测试例子&#xff08;源码使用&#xff09;将log4qt源码添加到工程测试代码日志配置文件&#xff1a;测试结果 总结log4qt更多请参考&#xff1a; 使用Log4Qt动态库一、创建Qt工程&#xff0c;命名为libLog4Qt二、在项目根目录下新建文件夹3r…

Simulink使能(Enable)、触发(Triggered)模块及其子系统的应用

Simulink中的使能&#xff08;Enable&#xff09;和触发&#xff08;Triggered&#xff09;模块及其子系统可以用于控制模型中的仿真运行时间和采样周期&#xff0c;从而提高模型的仿真效率和精度。 使用使能子系统 创建一个在控制信号为正值时执行的子系统。使用触发子系统 创…