Spring Security系列教程08--基于内存模型实现授权

简介: 前言在前面的几个章节中,一一哥 带大家学会了如何创建Spring Security项目,3种认证方式,以及在前后端分离时的安全处理方案。在之前的这几章节中,我们主要学习的还是关于 "认证" 的知识点,但是安全框架还有另一大核心功能,就是 "授权"!所以接下来请各位跟着 一一哥 来学习Spring Security的授权功能,只有 "认证" + "授权" 双剑合璧,才能真正发挥Spring Security的威力!这时候小伙伴会问,"一一哥,那啥是授权啊?我连授权还不知道呢,更别说怎么实现授权了,呜呜......"!别急,接下来咱们先看看授权的概念!一. 授权简介1. 授权概念所

前言

在前面的几个章节中,一一哥 带大家学会了如何创建Spring Security项目,3种认证方式,以及在前后端分离时的安全处理方案。在之前的这几章节中,我们主要学习的还是关于 "认证" 的知识点,但是安全框架还有另一大核心功能,就是 "授权"

所以接下来请各位跟着 一一哥 来学习Spring Security的授权功能,只有 "认证" + "授权" 双剑合璧,才能真正发挥Spring Security的威力!

这时候小伙伴会问,"一一哥,那啥是授权啊?我连授权还不知道呢,更别说怎么实现授权了,呜呜......"!别急,接下来咱们先看看授权的概念!

一. 授权简介

1. 授权概念

所谓授权,比如说某个用户想要访问某个资源(接口、页面、功能等),我们应该先去检查该用户是否具备对应的权限,如果具备就允许访问,如果不具备,则不允许访问。也就是说,授权是用来判断控制用户是否就有相应权限的问题的!

在我们认证之后,才会考虑授权的问题,也就是你得先登录进来,然后才有资格判断你有没有权限。就好比你想做倭国的首相,拥有首相的权力,前提是你得先进入倭国成为倭国人,否则何谈权利的分配?所以授权的前提,是已经经过了认证,这个我们需要注意哦!

2. 授权粒度

在开始授权之前,我先给大家普及一下授权粒度的概念,所谓的"粒度",通俗的说就是指级别、程度、大小的意思,授权粒度就是对授权细分的级别、程度的意思。

在Spring Security中,授权粒度有如下几种:

  • 支持基于 URL 的请求授权
  • 基于方法访问的授权
  • 基于对象访问的授权

一共3种授权粒度,但是对于我们开发来说,常用的是基于URL和方法两种粒度进行授权。

3. 授权实现方式

那么我们在开发时,到底该怎么进行授权呢?如果我们想实现授权,可以采用如下方式:

  • 基于内存模型实现授权
  • 基于默认数据库模型实现授权
  • 基于自定义数据库模型实现授权

在本文中,一一哥 先带大家基于内存模型来实现授权,但是对于该实现方案,我们真正开发时几乎不用!有的小伙伴又急了,"开发时不用,你带我们学它干嘛啊"?别急!这种实现方案,虽然在实际生产环境中很少用,但是如果我们只是想学习如何实现授权,只是想搞个案例demo玩玩,这种授权方式是最简单的实现方案!而且也有助于我们理解基于数据库模型的授权实现方案毕竟多学点没坏处,你就跟着我学呗。

二. 基于内存模型实现授权

1. 创建测试接口

在开始授权代码之前,我们先创建3个用于测试的Web接口,分别供3个不同的用户角色来进行操作。

创建/admin/hello接口:

@RestController@RequestMapping("/admin")
publicclassAdminController {
@GetMapping("/hello")
publicStringhello() {
return"hello, admin";
    }
}

创建/user/hello接口:

@RestController@RequestMapping("/user")
publicclassUserController {
@GetMapping("hello")
publicStringhello() {
return"hello, user";
    }
}

创建/visitor/hello接口:

@RestController@RequestMapping("/visitor")
publicclassVisitorController {
@GetMapping("/hello")
publicStringhello() {
return"hello, visitor";
    }
}

以上三个接口,我们规定如下:

  • /visitor/hello 任何人都可以访问;
  • /admin/hello 具有 admin 角色的人才能访问;
  • /user/hello 具有 user 角色的人才能访问;
  • 所有 user 角色能够访问的接口资源,admin 角色也都能够访问。

好了,准备工作现在我们就做完了。当然,你也可以把这3个接口写在一个Controller中,这都无所谓的!

2. 配置资源访问权限

接口都准备好了,接下来该干嘛呢?接下来如果我们要想实现上面的目标,我们需要先定义一个SecurityConfig配置类,在该类中对各接口进行授权配置。

@EnableWebSecurity(debug=true)
publicclassSecurityConfigextendsWebSecurityConfigurerAdapter {
@Overrideprotectedvoidconfigure(HttpSecurityhttp) throwsException {
http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .hasAnyRole("USER","ADMIN")
                .antMatchers("/visitor/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}

上面代码配置的含义是:

  • 如果请求的路径是 "/admin/**" 开头的接口,用户需要具有 admin 角色;
  • 如果请求的路径是 "/user/**" 开头的接口,用户需要具有 user 或 admin 角色;
  • 如果请求的路径是 "/visitor/**" 开头的接口,哪个用户都可以随便访问;
  • 剩余的其他格式的请求路径,必须认证(登录)后才可以访问。

注意:

代码中配置规则的顺序非常重要!和 Shiro 类似,Spring Security 在匹配的时候也是按照从上往下的顺序来进行匹配,一旦匹配到了就不再继续匹配了,所以拦截规则的顺序不能写错!

利用上面的配置规则,我们就可以对相应的接口进行限制了,这时候这些接口,就不是你想访问就能访问的了,你必须拥有对应的角色或者权限才行。

3. Ant匹配符

这里我们采用了 Ant 风格的路径匹配符,Ant 风格的路径匹配符在 Spring 家族中使用非常广泛,它的匹配规则如下:

这3个匹配符,需要各位记住,很简单的!

4. 基于内存模型创建用户角色

对各种接口进行限制后,接下来我们要给用户分配角色权限才行,否则用户是没办法访问有些接口的。那么怎么给用户分配角色呢?

在Spring Security4.x版本中,我们登陆的用户有一个默认的ROLE_USER角色,但是在Spring Security5.x版本中,把这个默认的角色给去掉了。此时访问我们的接口时,如果用户没有被分配相关的角色权限,即使用户登录成功,也未必可以访问接口。真操蛋啊!既然这样,没办法,我们只能自己给用户手动分配角色咯。

Spring Security中基于内存创建用户角色,有两种方式:

  • 在configure(AuthenticationManagerBuilder auth)方法中定义;
  • 创建UserDetailsService对象。

我这里会给大家分别介绍2种创建用户角色的方式,这两种方式其实用哪一种都可以,无所谓优劣,看个人习惯吧。

5. 创建分配角色的第一种方式

我们先来看看第一种实现方式,这个实现方式主要是利用AuthenticationManagerBuilder来进行实现。

在上面创建的SecurityConfig类中,我们添加一个新的configure()方法,创建用户,并给该用户分配用户名、密码、角色权限等信息,并对该用户的密码采用不加密。

@EnableWebSecurity(debug=true)
publicclassSecurityConfigextendsWebSecurityConfigurerAdapter {
    ......
@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth) throwsException {
auth.inMemoryAuthentication()
            .withUser(User.withUsername("admin").password("123").roles("ADMIN", "USER").build())
            .withUser(User.withUsername("user").password("123").roles("USER").build())
//设置密码编码器            .passwordEncoder(NoOpPasswordEncoder.getInstance());
    }
   ......     
}

通过这几行代码,我们就在内存中创建了2个用户,分别是admin和user用户,密码都是123!

6. 创建分配角色的第二种方式

掌握了第一种创建方式后,我们再来学习第二种创建方式,这种创建方式主要是利用InMemoryUserDetailsManager来来实现。我们还是在上面的SecurityConfig类中,编写相应代码。

@EnableWebSecurity(debug=true)
publicclassSecurityConfigextendsWebSecurityConfigurerAdapter {
   .......
/*** 内存中创建多个用户角色的方式2:* 基于内存的多用户支持.在内存中创建多个用户与角色.*/@BeanpublicUserDetailsServicecreateUserDetailService() {
InMemoryUserDetailsManagermanager=newInMemoryUserDetailsManager();
//在内存中创建admin与user用户manager.createUser(User.withUsername("admin").password("123").roles("ADMIN", "USER").build());
manager.createUser(User.withUsername("user").password("123").roles("USER").build());
returnmanager;
    }
/*** 由于5.x版本之后默认启用了委派密码编码器,因而按照以往的方式设置内存密码将会读取异常,所在需要暂时将密码编码器设置为NoOpPasswordEncoder.* 后面我们可以修改成BCryptPasswordEncoder.* 这里必须设置一个密码编码器,否则无法通过对用户名和密码的验证.*/@BeanpublicPasswordEncoderpasswordEncoder() {
//return new BCryptPasswordEncoder();returnNoOpPasswordEncoder.getInstance();
    }      
   .......     
}

这种方式中,我们创建用户和分配密码,与第一种方式基本类似,但是密码编码器需要单独配置,通过@Bean直接生成PasswordEncoder对象即可。

7. 代码结构

至此,我们在内存中创建用户,并分配角色信息的功能就实现了,以上项目完整代码结构如下图所示。

8. InMemoryUserDetailsManager类结构

在刚才第2种实现方案中,我们涉及到了一个InMemoryUserDetailsManager类,这个类是怎么回事呢?我们一起来看看该类的类结构吧。

InMemoryUserDetailsManager是UserDetailsService接口中的一个实现类,InMemoryUserDetailsManager的类结构图如下:

它可以把用户数据保存在内存中,在一些不需要引入数据库的场景下很有用,也就是如果我们不想持久化保存用户角色信息,用InMemoryUserDetailsManager类的createUser()方法生成用户,并赋予相应的角色即可。

三. 功能验证

我们把项目启动起来,然后以不同的身份登录进来,分别访问不同的接口,当某个用户在不具备相应角色时,会出现403提示信息。只有具有相应的角色权限时,才可以访问对应的接口。

到此为止,一一哥 就带大家在内存里创建了用户,并给该用户分配了用户名、密码、角色等,这就是所谓的在内存中给用户授权。并没有什么复杂的实现吧,你学会了吗?如果有什么疑问,请在评论区留言!如果你觉得挺不错的,码字不易,请给个赞呗!

相关文章
|
1月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
160 5
|
2月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
31 2
|
3月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
134 2
|
4月前
|
XML JavaScript Java
Spring Retry 教程
Spring Retry 是 Spring 提供的用于处理方法重试的库,通过 AOP 提供声明式重试机制,不侵入业务逻辑代码。主要步骤包括:添加依赖、启用重试机制、设置重试策略(如异常类型、重试次数、延迟策略等),并可定义重试失败后的回调方法。适用于因瞬时故障导致的操作失败场景。
Spring Retry 教程
|
3月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
|
3月前
|
存储 前端开发 Java
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
Kotlin教程笔记 - MVVM架构怎样避免内存泄漏
63 0
|
5月前
|
Java 数据库连接 Spring
一文讲明 Spring 的使用 【全网超详细教程】
这篇文章是一份全面的Spring框架使用教程,涵盖了从基础的项目搭建、IOC和AOP概念的介绍,到Spring的依赖注入、动态代理、事务处理等高级主题,并通过代码示例和配置文件展示了如何在实际项目中应用Spring框架的各种功能。
一文讲明 Spring 的使用 【全网超详细教程】
|
3月前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
详细介绍实现Java Spring Boot FCM推送教程
119 0
|
5月前
|
SQL Java 数据库连接
Spring Boot联手MyBatis,打造开发利器:从入门到精通,实战教程带你飞越编程高峰!
【8月更文挑战第29天】Spring Boot与MyBatis分别是Java快速开发和持久层框架的优秀代表。本文通过整合Spring Boot与MyBatis,展示了如何在项目中添加相关依赖、配置数据源及MyBatis,并通过实战示例介绍了实体类、Mapper接口及Controller的创建过程。通过本文,你将学会如何利用这两款工具提高开发效率,实现数据的增删查改等复杂操作,为实际项目开发提供有力支持。
336 0
|
6月前
|
SQL Java 调度
实时计算 Flink版产品使用问题之使用Spring Boot启动Flink处理任务时,使用Spring Boot的@Scheduled注解进行定时任务调度,出现内存占用过高,该怎么办
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。

热门文章

最新文章