springsecurity教程

2023-11-17

springsecurity 课程

一、权限管理简介

1、什么是权限管理

基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。

​ 权限管理包括用户身份认证鉴权(授权)两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。

2、认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。

image-20210823091654008

3、授权

授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。

下图中橙色为授权流程。

image-20210823092303974

二、权限管理解决方案

1、基于角色的访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

image-20210823093938094

上图中的判断逻辑代码可以理解为:

if(主体.hasRole("总经理角色id")){
     查询工资
}

缺点:以角色进行访问控制粒度较粗,如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断主体的角色是否是总经理或部门经理”,系统可扩展性差。

修改代码如下:

if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){
     查询工资
}

2、基于资源的访问控制

​ RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制,比如:主体必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

上图中的判断逻辑代码可以理解为:

if(主体.hasPermission("查询工资权限标识")){
     查询工资
}

优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。

三、Spring Security概述

1,Spring Security简介

Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security 拥有以下特性:

  • 对身份验证和授权的全面且可扩展的支持
  • 防御会话固定、点击劫持,跨站请求伪造等攻击
  • 支持 Servlet API 集成
  • 支持与 Spring Web MVC 集成

Spring、Spring Boot 和 Spring Security 三者的关系如下图所示:

2、Spring Security快速入门

2.1、引入依赖

<!--springboot整合security坐标-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2.2、创建一个控制器

@RestController
public class HelloController {
    @GetMapping("hello")
    public String hello(){
        return "Hello Spring security";
    }
}

2.3、启动项目

访问:http://localhost:8080/hello 结果打开的是一个登录页面,其实这时候我们的请求已经被保护起来了,要想访问,需要先登录。

Spring Security 默认提供了一个用户名为 user 的用户,其密码在控制台可以找到

四、Spring Security 认证配置

1、WebSecurityConfigurerAdapter

当然还可以通过配置类的方式进行配置,创建一个配置类去继承,实现自定义用户名密码登录

/**
 * Spring Security配置类
 * 在springboot2.7 后WebSecurityConfigurerAdapter弃用了,用2.5.4
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 对密码进行加密。123 是密码明文,现在 Spring Security 强制性要求『不允许明文存储密码』。
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin");
    }
}

springsecurity强制性要求必须使用密码加密器(PasswordEncoder)对原始密码(注册密码)进行加密。因此,如果忘记指定 PasswordEncoder 会导致执行时会出现 There is no PasswordEncoder mapped for the id "null" 异常。

这是因为我们在对密码加密的时候使用到了 BCryptPasswordEncoder 对象,而 Spring Security 在对密码比对的过程中不会『自己创建』加密器,因此,需要我们在 Spring IoC 容器中配置、创建好加密器的单例对象,以供它直接使用。

所以,我们还需要在容器中配置、创建加密器的单例对象(上面那个 new 理论上可以改造成注入),修改Spring securitry配置类

/**
 * Spring Security配置类
 * 在springboot2.7后WebSecurityConfigurerAdapter弃用了,用2.5.4
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 对密码进行加密。123 是密码明文,现在 Spring Security 强制性要求『不允许明文存储密码』。
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin");
    }
       /**
     * 将PasswordEncoder注入到ioc容器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Spring Security 内置的 Password Encoder 有:

加密算法名称 PasswordEncoder
NOOP NoOpPasswordEncoder.getInstance()
SHA256 new StandardPasswordEncoder()
BCRYPT(官方推荐) new BCryptPasswordEncoder()
LDAP new LdapShaPasswordEncoder()
PBKDF2 new Pbkdf2PasswordEncoder()
SCRYPT new SCryptPasswordEncoder()
MD4 new Md4PasswordEncoder()
MD5 new MessageDigestPasswordEncoder(“MD5”)
SHA_1 new MessageDigestPasswordEncoder(“SHA-1”)
SHA_256 new MessageDigestPasswordEncoder(“SHA-256”)

2、UserDetailsService

  • 在service包下创建一个UserDetailsService类

    /**
     * spring security认证业务类
     */
    @Service
    public class MyUserDetailsService implements UserDetailsService {
       
        @Autowired
        private UserDao userDao;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
               //调用dao到数据库中根据username查找用户信息
            Users users = userDao.getByUserName(username);
            String anths = String.join(",", userInfo.getAnths());
             try {
                //将查找到的用户帐号与密码保存到Security的User对象中由Security进行比对
                return new User(users.getUsername(), passwordEncoder.encode(users.getPassword()),
                        //配置登录用户有哪些角色和权限,此处模拟直接写死
                        AuthorityUtils.commaSeparatedStringToAuthorityList(anths);
            }catch (Exception e){
                throw  new UsernameNotFoundException("用户"+username+"不存在");
            }
        }
    }
    
  • 修改spring security配置类

    
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private MyUserDetailsService userDetailsService;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
           auth.userDetailsService(userDetailsService);
        }
         
        /**
         * 将PasswordEncoder注入到ioc容器
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    

3、Spring Security 自带的表单认证

  • 准备自定义登录页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>登录</title>
    </head>
    <body>
    <form action="dologin" method="post">
        <!--注意:帐号和密码的名称必须是username和password否则spring security无法识别-->
        <p>帐号:<input type="text" name="username"></p> 
        <p>密码:<input type="text" name="password"></p>
        <p><button type="submit">登录</button></p>
    </form>
    </body>
    </html>
    
  • SpringSecurityConfig 类中的配置代码

      @Override
        protected void configure(HttpSecurity http) throws Exception {             
    
            http.formLogin()
                    .loginPage("/login.html")//配置需要显示的登录页面
                    .loginProcessingUrl("/dologin") //配置登录请求路径,很from表单的 action 要对应上
                    .permitAll()//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权
            
                    .and().authorizeRequests()
                    .anyRequest().authenticated();  // 除了antMatchers() 配的页面,其他都需要认证
            
            http.csrf().disable();
        }
    

五、鉴权配置

1、鉴权配置

权限表达式 说明
permitAll() 永远返回 true
hasRole(“role”) 当用户拥有指定身份时,返回 true
hasAnyRole(“role1”, “role2”, …) 当用户返回指定身份中的任意一个时,返回 true
hasAuthority(“authority1”) 当用于拥有指定权限时,返回 true
hasAnyAuthority(“authority1”, “authority2”) 当用户拥有指定权限中的任意一个时,返回 true

hasRole():数据库用户角色必须加 ROLE_ 前缀,而用hasRole() security会自动加上ROLE_ 前缀,自己不能加上ROLE_ 前缀,例如

AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admin")
对上
hasRole("admin")

hasAuthority() 数据库角色名称和方法内容一致 例如:

AuthorityUtils.commaSeparatedStringToAuthorityList("admin")
对上
hasRole("admin")

没有权限跳转到自定义页面

        http.formLogin()
                .loginPage("/hello")//配置需要显示的登录页面
                .loginProcessingUrl("/dologin") //配置登录请求路径
                .permitAll()//这句配置很重要,新手容易忘记。放开 login.html和dologin 的访问权

                .and().authorizeRequests()
                .antMatchers("/","/hello").permitAll()// 设置哪些路劲不需要登录,能直接当问
                .antMatchers("/toupdate").hasAuthority("admin") //设置资源具有指定角色才能访问
                .anyRequest().authenticated();  // 除了antMatchers() 配的页面,其他都需要认证

        http.csrf().disable();

2、使用注解实

在实际的使用过程中用户的鉴权并不是通过置来写的而是通过注解来进行,Spring Security 默认是禁用注解的。

要想开启注解功能需要在配置类上加入 @EnableGlobalMethodSecurity注解来判断用户对某个控制层的方法是否具有访问权限。

注解就是用来替换springSecurity配置类中的http.authorizeRequests()配置

Spring Security 支持三套注解:

注解类型 注解
jsr250 注解 @DenyAll、@PermitAll、@RolesAllowed
secured 注解 @Secured
prePost 注解 @PreAuthorize、@PostAuthorize

使用什么注解在@EnableGlobalMethodSecurity开启,默认是关闭的,例如

@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled  = true,securedEnabled=true) //开启jsr250和secured注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
  • secured 注解

@Secured 注解是 jsr250 标准出现之前,Spring Security 框架自己定义的注解。

@Secured 标注于方法上,表示只有具有它所指定的角色的用户才可以调用该方法。如果当前用户不具备所要求的角色,那么,将会抛出 AccessDenied 异常,注解和配置类都要加上ROLE_ 前缀

@RestController
public class UserController {
    @Secured({"ROLE_USER","ROLE_ADMIN"}) // 这里加ROLE_前缀 
    @RequestMapping("/all-can-do")
    public String show7(){
        return "all-can-do";
    }

    @Secured("ROLE_USER") 
    @RequestMapping("/admin-can-do")
    public String show6(){
        return "admin-can-do";
    }
}

配置类

   return new User(users.getUsername(), passwordEncoder.encode(users.getPassword()),
                 AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); //配置类也要加上ROLE_前缀
  • JSR-250 注解

    @DenyAll: 所有用户都不可以访问

    @PermitAll:所有用户都可以访问

​ @RolesAllowed:用法同@Secured差不多,区别是注解上ROLE_ 可加可不加,但是配置类上必须加ROLE_ 前缀

  • prePost 注解

    @PreAuthorize可以用来控制一个方法是否能够被调用。

    @PreAuthorize(hasAuthority(‘admin’))
    publicvoid addUser(User user) {
    	System.out.println(“addUser…” + user);
    }
    

3、 登录返回处理

​ 在某些前后端完全分离,仅靠 JSON 完成所有交互的系统中,一般会在登陆成功时返回一段 JSON 数据,告知前端,登陆成功与否。可以通过实现 AuthenticationSuccessHandler 处理登录成功,实现AuthenticationFailureHandler 处理登录失败

  • 创建SimpleAuthenticationSuccessHandler和SimpleAuthenticationFailureHandler类来处理登录成功和失败的业务

     httpServletResponse.setContentType("application/json;charset=UTF-8");
            PrintWriter pw = httpServletResponse.getWriter();
            String success = JSON.toJSONString(ResponseResult.LOGIN_FAIL);
            pw.print(success);
            pw.flush();
            pw.close();
    
  • 修改spring security配置类

    
          //登录相关配置
            http.formLogin()
                    .loginPage("/login.html")
                    .loginProcessingUrl("/dologin") 
                    .successHandler(new simpleAuthenticationSuccessHandle())//配置登录成功后的处理器
                    .failureHandler(new SimpleAuthenticationFailureHandler())//配置登录失败后的处理器
                    .permitAll();
    
        }
    

4、鉴权的异常处理

Spring Security 的认证工作是由 FilterSecurityInterceptor 处理的。FilterSecurityInterceptor 会抛出 2 种异常:

  • 在用户 “该登录而未登录” 时,抛出 AuthenticationException 异常;实现AuthenticationEntryPoint

  • 在用户 “权限不够” 时,抛出 AccessDeniedException 异常。实现AccessDeniedHandler

  • 修改SpringSecurity配置类

     //认证和鉴权异常配置
     http.exceptionHandling()
        .authenticationEntryPoint()//认证异常处理器
        .accessDeniedHandler();//鉴权异常处理器
    
    

5、退出操作

Spring Security中发送了logout请求成功后会自动跳转到默认的login.html页面。在前后端分离的项目中,所有的页面跳转都是由前端控制的,服务器端只需要返回一个json的状态码即可,实现 LogoutSuccessHandler

  • Spring Security配置类

       //前后端项目中要禁用掉session
      http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      //退出成功后的处理器
      http.logout().logoutSuccessHandler(new SimpLogoutSuccessHandler());
    
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springsecurity教程 的相关文章

随机推荐

  • [940]TensorFlow练习4: CNN, Convolutional Neural Networks

    Convolutional Neural Networks翻译为卷积神经网络 常用在图像识别和语音分析等领域 CNN详细介绍参看 https en wikipedia org wiki Convolutional neural networ
  • 太原理工大学c语言期末试卷及答案,太原理工大学人工智能复习题 试题 答案概要...

    任务有何不同 6 5 基于规则的专家系统是如何工作的 其结构为何 6 6 基于框架的专家系统与面向目标编程有何关系 其结构有何特点 其设计任务是什 么 6 7 为什么要提出基于模型的专家系统 试述神经网络专家系统的一般结构 6 8 新型专家
  • 在区块链的世界里,美国CFTC希望成为一个节点

    在华盛顿参与一场区块链峰会时 CFTC 美国商品期货交易委员会 主席J Christopher Giancarlo发表了题为 数字三要素 技术 监管和市场 的演讲 Giancarlo在演讲中提到了区块链技术的应用可能 认为如果2008年经济
  • Component name “Nearby“ should always be multi-word. 的原因

    很好奇在ESlint规范中为啥要求component的name不能是单个单词 除了App外 呢 查阅官方文档得知 This rule require component names to be always multi word excep
  • spring控制反转与依赖注入

    1 什么是控制反转 传统的编程思路是 当我需要某个对象时 我便自己去实例化调用它 而控制反转则是 当我需要某个对象时 自然有人帮我们实例化它 简单的来说 这是一种衣来张口 饭来伸手式的控制模式 这也符合spring最根本的使命 简化java
  • 基于Java开发一套完整的区块链系统

    一 区块链技术理论基础 1 基本概念 1 区块链 从技术层面来看 区块链是由包含交易信息的区块按照时间顺序从后向前有序链接起来的数据结构 从应用层面来说 区块链是一个分布式的共享账本和数据库 具有去中心化 不可篡改 全程留痕 集体维护 公开
  • 删除计算机用户时拒绝访问权限,c盘为什么拒绝访问 删除c盘文件需要管理员权限怎么办...

    c盘是电脑中的关键位置 存储着很多系统重要文件 如果电脑出问题一般就是c盘中的文件异常 近日有小伙伴出现这样一个问题 打不开c盘显示拒绝访问 作为计算机的主人被无法访问 这种问题该怎么解决呢 其实很简单 只要找准方向就可以迎刃而解 下面小编
  • 8月热门论文丨AI Agent会是大模型的未来发展方向吗?

    过去的8月 如果让我用一个词来总结 那就是 Agent 大模型的下半场已经拉开序幕 大厂们都纷纷表态入局 Agent OpenAI创始成员Andrej Karpathy表示相比大模型 OpenAI内部目前已经关注Agent领域 亚马逊也宣布
  • 13:js逆向-登录加密(aes加密)

    post请求 请求头信息被加密 response返回数据被加密 1 首先搞请求头data加密 还是直接搜索 搞定加密的参数 f body loginMethod 1 name 17756236589 password 132456789 h
  • wpf解决方案

    Wpf部分 1wpf textbox 显示和隐藏 personq Visibility Visibility Visible 这样显示 personq Visibility Visibility Hidden 这样隐藏 2 wpfradio
  • Linux笔记之安装配置nginx的两种方式——yum安装和源码安装

    安装配置nginx的两种方式 yum安装和源码安装 访问nginx的官方网站 http www nginx org Nginx版本类型 Mainline version 主线版 即开发版 Stable version 最新稳定版 生产环境上
  • Cannot run program “D:\jdk8\bin\java.exe“ (in directory “C:\Users\Administrator\AppData\Local\JetBra

    bug笔记 项目场景 运行main方法 Cannot run program D jdk8 bin java exe in directory C Users Administrator AppData Local JetBrains In
  • 蓝桥杯真题 杨辉三角形 python

    专栏 蓝桥杯题目 目录 问题描述 输入格式 输出格式 样例输入 样例输出 评测用例规模与约定 省流版本 题目解析 综上所述 写成代码如下所示 问题描述 下面的图形是著名的杨辉三角形 如果我们按从上到下 从左到右的顺序把所有数排成一列 可以得
  • Unity入门教程

    Unity入门 Unity脚本基础 Unity脚本基本规则 Unity脚本生命周期函数 Inspector窗口可编辑的变量 Mono中的重要内容 Unity重要组件和API 最小的单位GameObject 时间相关Time 1 时间缩放比例
  • 浅谈编程职业的乐趣和苦恼

    乐趣 编程为什么有趣 作为回报 它的从业者期望得到什么样的快乐 首先 这种快乐是一种创建事物的纯粹快乐 如同小孩在玩泥巴时感到快乐一样 成年人喜欢创建事物 特别是自己进行设计 我想这种快乐是上帝创造世界的折射 一种呈现在每片独特的 崭新的树
  • SpringCloud 商城系统搭建之eureka

    项目环境 1 IDE eclipse maven 2 操作系统 win10 3 jdk 1 8 4 springboot 2 1 0 RELEASE springcloud Greenwich SR5 SpringCloud对应Spring
  • BIG DATA 神奇的大数据 - HDFS分布式文件系统

    目录 自说 学习路径 基本概念 块 优劣 结构 读写流程 使用 命令行接口 自说 HDFS在Hadoop起到重要作用 解决了大规模的数据存储及管理问题 呢么有如此庞大的数据 hdfs是如何准确的做到数据的保存与不流失性 又是通过什么方式去存
  • iqoo手机可以刷鸿蒙系统吗,华为老机型可以更新鸿蒙系统么-华为哪些老机型支持鸿蒙系统...

    目前由于华为鸿蒙系统发布的火爆 导致花粉俱乐部直接崩溃 那么鸿蒙系统除了支持当下的旗舰机外 还支持老机型么 小编就为大家 带来了相关说明 华为老机型可以更新鸿蒙系统么 可以 据小编得到的消息 鸿蒙系统将会支持百万老机型 更新 不要错过了 华
  • Docker常用命令分享(Docker安装MySQL)

    一 Docker是什么 Docker 使用 Google 公司推出的 Go 语言 进行开发实现 基于 Linux 内核的 cgroup namespace 以及 OverlayFS 类的 Union FS 等技术 对进程进行封装隔离 属于
  • springsecurity教程

    springsecurity 课程 一 权限管理简介 1 什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理 权限管理属于系统安全的范畴 权限管理实现对用户访问系统的控制 按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授