1 问题 如何保护我们的程序?
1.1 创建code目录
目的:后面的security工程均在此目录下学习
创建code目录,并使用idea打开
1.2 不使用安全框架的springboot web程序
1.2.1 新建子模块springboot-01-hello
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uAeASGKn-1681958373972)(https://cdn.nlark.com/yuque/0/2023/png/32441366/1681955131092-bbc460d1-dc01-4b6a-bdf3-38a7a06ee7be.png#height=303&width=415)]
1.2.2 添加依赖和maven插件等
pom.xml 中部分内容如下,此处使用bootpom模板创建并粘贴
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.6.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--springboot web 程序 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--自动生成get、set和日志对象的lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.2.3 新建启动类
com.powernode包下新建启动类Application,可以使用bootmain模板,学员自行创建
1.2.4 新建三个controller
com.powernode.controller包下新建三个controller
学生
@RestController
@RequestMapping("/student")
public class StudentController {
@GetMapping("/query")
public String queryInfo(){
return "I am a student,My name is Eric!";
}
}
教师
@RestController
@RequestMapping("/teacher")
public class TeacherController {
@GetMapping("/query")
public String queryInfo(){
return "I am a teacher,My name is Thomas!";
}
}
管理员
@RestController
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/query")
public String queryInfo(){
return "I am a administrator, My name is Obama!";
}
}
1.2.5 启动程序
启动程序的快捷键:Ctrl+Shift+F10
1.2.6 访问程序
通过浏览器访问程序
| http://localhost:8080/student/query
http://localhost:8080/teacher/query
http://localhost:8080/admin/query |
---|
通过curl访问(可以使用git bash)
| # curl 默认发出get请求,下面可以省略 -X GET选项
curl -X GET localhost:8080/teacher/query |
---|
返回效果如下:
curl 的使用可以作为扩展学习(不讲,学员自学)
| curl 是常用的命令行工具,用来请求 Web 服务器。它的名字就是命令行(Command Line)下的 URL 工具的意思。
它的功能非常强大,命令行参数多达几十种。如果熟练的话,完全可以取代 Postman 这一类的图形界面工具。
参考学习网站:
https://catonmat.net/cookbooks/curl
https://www.ruanyifeng.com/blog/2019/09/curl-reference.html
https://www.ruanyifeng.com/blog/2011/09/curl.html
https://blog.csdn.net/angle_chen123/article/details/120675472
1.2.7 结论
此示例说明:
没有加入安全框架的SpringBoot web程序,默认所有资源均不受保护。 |
---|
1.2.8 问题
我们的项目很多资源必须被保护起来,如何保护?引入安全框架 |
---|
2 认证授权等基本概念
2.1 认证(authentication)
2.1.1 系统为什么要认证?
认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
2.1.2 什么是认证(登录)?
认证 :用户认证就是判断一个用户的身份是否合法的过程。
2.1.3 常见的用户身份认证方式
Ø 用户名密码登录
Ø 二维码登录
Ø 手机短信登录
Ø 指纹认证
Ø 人脸识别
Ø 等等…
2.2 会话(session)
2.2.1 什么是会话
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保存在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于****session方式、基于token方式等。
2.2.2 基于session的认证方式
它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客户端的sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id也就无效了。
2.2.3 基于token的认证方式
它的交互流程是,用户认证成功后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。可以使用Redis 存储用户信息(分布式中共享****session)。
基于session的认证方式由Servlet规范定制,服务端要存储session信息需要占用内存资源,客户端需要支持cookie;基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式。如今移动互联网时代更多类型的客户端需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于****token的方式更适合。
2.3 授权(authorization)
2.3.1 为什么要授权(控制资源被访问)?
因为不同的用户可以访问的资源是不一样的。
2.3.2 什么是授权(给用户颁发权限)
授权: 授权是用户认证通过后,根据用户的权限来控制用户访问资源的过程。
拥有资源的访问权限则正常访问,没有权限则拒绝访问。
2.4 RBAC(Role-Based Access Control) 基于角色的访问控制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSusII0c-1681958373975)(https://cdn.nlark.com/yuque/0/2023/png/32441366/1681955131923-fbaab2ec-0224-43ed-bc32-7d1dcda26022.png#height=198&width=415)]
用户,角色,权限 本质:就是把权限打包给角色(角色拥有一组权限),分配给用户(用户拥有多个角色)。
最少包括五张表 (用户表、角色表、用户角色表、权限表、角色权限表)
3 java的安全框架实现
主要有三种方式:
| Ø Shiro:轻量级的安全框架,提供认证、授权、会话管理、密码管理、缓存管理等功能
Ø Spring Security:功能比Shiro强大,更复杂,权限控制细粒度更高,对OAuth2 支持更好,与Spring 框架无缝集合,使Spring Boot 集成很快捷。
Ø 自己写:基于过滤器(filter)和AOP来实现,难度大,没必要。 |
---|
4 Spring Security
4.1 什么是Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式(注解)的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
以上解释来源于百度百科。可以一句话来概括:
SpringSecurity 是一个安全框架。 |
---|
4.2 官方网址
https://spring.io/projects/spring-security |
---|
查看下官网
5 认证入门
5.1 安全入门项目
5.1.1 新建模块springsecurity-02-hello
5.1.2 添加依赖和maven插件
此处可以使用bootpom 模板创建临时文件,并拷贝。
5.1.3 添加spring-boot-starter-security依赖
|
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
5.1.4 新建启动类
新建启动类Application,学员自行创建
5.1.5 新建三个controller
参照1.2.4 创建,可以直接拷贝过来
小提示:能使用键盘的地方,多使用键盘,少使用鼠标
重命名快捷键(shift+F6),移动文件快捷键 F6 |
---|
5.1.6 启动程序,并使用浏览器访问
http://localhost:8080/student/query |
---|
系统会跳转到登录页面
运行结果说明:
spring Security默认拦截了所有请求,但登录退出不拦截
5.1.7 登录系统
使用默认用户user登录系统,密码是随机生成的UUID字符串,可以在控制台(console)上找到。
登录系统,再次访问:
|http://localhost:8080/student/query
http://localhost:8080/teacher/query
http://localhost:8080/admin/query
发现登录后的用户均可以正常访问
5.1.8 退出系统
http://localhost:8080/logout |
---|
单击Log Out 按钮,成功退出
5.1.9 结论
| 引入spring-boot-starter-security依赖后,项目中除登录退出外所有资源都会被保护起来
认证(登录)用户可以访问所有资源,不经过认证用户任何资源也访问不了。 |
---|
5.1.10 问题
所有资源均已保护,但是用户只用一个,密码是随机的,只能在开发环境使用
5.2 使用配置文件配置用户名和密码
5.2.1 新建模块springsecurity-03-configfile
模仿5.1.1 新建即可。
5.2.2 添加安全依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
5.2.3 新建启动类
新建启动类Application,学员自行创建
5.2.4 新建三个controller
参照1.2.4 创建,可以直接拷贝过来
5.2.5 新建配置文件application.yml
添加spring security 配置信息
spring:
security:
user:
name: admin
password: 888888
5.2.6 启动运行并使用浏览器测试
发现使用配置文件中的用户名和密码可以正常访问。
配置文件中配置用户后,默认的user用户就没有了。
示例说明:可以通过配置文件配置用户和密码,解决了使用随机生成密码的问题。
5.2.7 查看源码
问题:
Ø 查看源码的快捷键是什么?
Ø 反编译生成代码和源码的区别?
Ø 如何下载源代码?
Ø 下载后的源代码存储到哪去了?
查看源码的快捷键:ctrl+鼠标左键
application.yml 中将鼠标指定到name那,按住ctrl键,单击鼠标左键
单击上图中 Download Sources,下载源码文件
可以看到默认用户名为user,密码为使用UUID随机生成的字符串。
5.2.8 问题
Spring Security配置文件中默认配置用户是单一的用户,大部分系统都有多个用户,多个用户如何配置? |
---|
5.3 基于内存的多用户管理
5.3.1 新建模块springsecurity-04-inmemory
5.3.2 添加安全依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
5.3.3 新建启动类
新建启动类Application,学员自行创建
5.3.4 新建三个controller
参照1.2.4 创建,可以直接拷贝过来
5.3.5 新建配置文件application.yml
参照5.2.5 ,可以直接拷贝内容
5.3.6 新建配置类
com.powernode.config包下新建配置类MySecurityUserConfig,如下:
@Configuration
public class MySecurityUserConfig {
@Bean
public UserDetailsService userDetailService() {
// 使用org.springframework.security.core.userdetails.User类来定义用户
//定义两个用户
UserDetails user1 = User._builder_().username("eric").password("123456").roles("student").build();
UserDetails user2 = User._builder_().username("thomas").password("123456").roles("teacher").build();
//创建两个用户
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(user1);
userDetailsManager.createUser(user2);
return userDetailsManager;
}
}
5.3.7 启动程序测试
登录页面输入用户名(thomas)和密码(123456),然后单击登录后,控制台报错,如下:
报错的原因如下:
这个是因为spring Sercurity强制要使用密码加密,当然我们也可以不加密,但是官方要求是不管你是否加密,都必须配置一个密码编码(加密)器
5.3.8 添加密码加密器bean但是不对密码加密
在MySecurityUserConfig类中加入以下bean
/*
*从 Spring5 开始,强制要求密码要加密
*如果非不想加密,可以使用一个过期的 PasswordEncoder 的实例 NoOpPasswordEncoder,
*但是不建议这么做,毕竟不安全。
*@return
*/
@Bean
public PasswordEncoder passwordEncoder(){
//不对密码进行加密,使用明文
return NoOpPasswordEncoder._getInstance_();
}
重启程序再次使用thomas/123456登录测试,可以登录正常访问了。
使用admin/888888登录,登录不成功,说明:我们只要添加了安全配置类,那么我们在****yml里面的配置就失效了
此处可以查看一下NoOpPasswordEncoder源码,再看一下单例模式,加密和密码对比方法
英文小提示:
明文:plaintext
密文:ciphertext
问题:
Ø 密码为什么要加密?加密的方式有哪些? 涉及到密码加密问题
Ø NoOpPasswordEncoder此类已经过期,而且还没有加密,如何解决?下章解决
Ø 以学生身份登录,发现不但可以访问学生的页面,还可以访问教师的页面和管理员的页面,如何解决? 权限问题,后面解决
Ø 如果要动态的创建用户,或者修改密码等(不是把用户名和密码写死到代码中),怎么办? 认证信息要存储到数据库中。
下面一章讲解密码加密问题