续Spring安全框架
查询用户权限
根据当前登录用户的 id 查询 该用户的 所有权限 的操作的sql语句
鼠标右键选中 运行
五表联查
因为这个查询结果可以赋给 permission.list 集合
要实现用户登录,
需要: 根据用户名获得用户信息 和 上面的 根据用户id获得用户所有权限的方法
在UserMapper中添加代码
编写测试类 测试一下上面的方法
实现登录业务
Spring-Security 是一个安全框架,它内部封装了登录验证的过程,无需我们编写,我们只需要将登录需要的数据或信息传递给Spring-Security ,它就会自动进行判断 并返回登录成功还是失败的结果
我们传递到Spring-Security 框架中信息的格式是 UserDetails 接口类型的对象,UserDetails 类型是 Spring-Security 提供的,我们要想实现登录就需要获得这个对象 并对他赋必要值
所以 需要创建一个类,实现Spring-Security 提供的 UserDetailsService 接口,并重写接口中方法,方法中编写代码,具体代码如下
代码都是 固定写法,记住怎么使用就行
i++ 和 ++i , 都是先自增,但是 i++ i是自增之前的值,++i i是自增之后的值
在 Spring-Security 配置类中,编写上面方法的调用,最终实现数据库登录效果
这的代码可以删除了
重启项目
拿前面的 tom、jerry都登录不了了,必须要用数据库 存在的用户登录
密码都是 888888
捋一下流程:
当点击 Sign in ,就把用户名和密码提交给Spring-Security了
Spring-Security拿着你的信息,去调用这个方法了
就会运行 userDetailsService 的 loadUserByUsername ,
并把用户输入的用户名发送给 loadUserByUsername 的参数,
在数据库中 查询 wangkj 的所有信息,
然后按步骤 1 2 3 4 5
又返回给
Spring-Security 接收到userDetails 之后,
和输入的密码 比对 匹配 (输入的密码是明文的,数据库里查 的是加密的)
匹配成功,登录成功; 匹配失败,登录失败
这个过程全都被封装在 Spring-Security 内部了,咱不用管,只用点个sign in
设置 Spring-Security 授权范围
所谓控制授权范围
就是控制当前网站哪些页面资源可以直接访问,哪些需要登录才能访问
假设我们设置网站首页 index_student.html 页面 不需要登录 就能直接访问
需要进行如下设置
在 SecurityConfig类中,重写另一个方法,设置授权范围
设置上面代码之后
重启项目,不登录也能访问学生首页
之前是需要登录,登录成功后 才能访问,现在 直接敲回车 就能访问
自定义登录页面
当前我们项目的登录页面
对比:
Spring-Security 给的默认登录页面,功能太少了,用户体验很差
下面学习 怎么把自己的 login.html 页面 替换为默认的登录页面
先来看一下当前我们项目的登录页面代码,不修改这里的代码
需要注意这几点:
- 表单提交的路径是 /login,配置时那里也是 /login
- 用户名是 username,这个名称是和Spring-Security约定好的,不能变
- 密码是 password,这个名称是和Spring-Security约定好的,不能变
然后我们开始配置,还是在 configure(HttpSecurity http) 方法中
重启项目:
访问必须 登录的页面
登录失败
登出
用户注册
开发业务流程:
从控制器(controller) 调 业务逻辑层(service),业务逻辑层 调 数据访问层(mapper)。
前面的 vrd 项目没有业务逻辑层(service),直接 controller 调mapper,这个做法在企业中是不允许的,不能跳层
接下来 业务逻辑层 怎么做,前端 VUE的绑定,从 用户注册 开始学第一个业务;
前面的 登录 受到框架的约束,很多东西都是固定的
先看一下 注册页面:
邀请码就是 班级号码
用户注册功能分析
之前是 控制器(controller) 直接调 数据访问层(mapper) 即持久层,
现在是 控制器(controller) 调 业务逻辑层(service),业务逻辑层 调 数据访问层(mapper)即持久层 ,持久层 再连数据库
控制器(controller) 只做和 前台页面的交互,
就是第5步,前台页面把 表单信息发送给控制器,控制器(controller)接收,控制器(controller) 将接收到的 表单信息 发送给业务层;
注册完了,成功还是失败,再把结果 发送给页面;
控制器 只做这两件事,① 接收表单信息 ② 把结果返回给前台页面
其它的一概不管,控制器只需要调业务逻辑层的方法 。
业务逻辑层 service 中写 密码加密、默认值的设置、创建时间的设置、是不是锁定、是不是可用 这些值的设置(业务逻辑层的代码是最多的),业务逻辑层中再调 mapper(mapper 就是接口 )
具体的业务流程:
-
用户访问注册页面,并填写表单信息
-
用户点击注册按钮,注册信息发送到 SystemController
-
SystemController 调用 Service 层的方法,将用户注册信息发送过去
-
业务逻辑层中处理 注册逻辑,包括验证邀请码,验证用户名是否已经被注册和密码加密等操作
-
业务逻辑层执行新增用户到数据库
-
业务逻辑层返回注册结果到控制层
-
控制层将注册结果返回页面,页面做相应处理(注册成功 / 注册失败)
注意:
前期 js文件、VUE、html 这些先不用太纠结,等把后台 业务逻辑层、控制层、数据访问层 都捋顺了,再去写 js。 先把时间放在后台Java这里
显示注册页面
设置用户 在没有登录的情况下 也能访问注册页面,
找到放行路径设置 添加放行
重启项目,测试 不用登录,能否访问注册页面
点击 新用户注册,能跳转到 注册页面,证明放行设置 成功了
看一下注册页面 代码,不修改这里的代码
注意这 几 点:
一个 表单提交,要在控制器(controller)接收,怎么接? 怎么把用户输入的邀请码,用户名,昵称,密码,确认密码(上面name 那5个) 接收到 ?
写一个实体类
实体类包model 中的 User类,没有邀请码,没有 确认密码
所以 针对这 5个,创建一个类,但这个类不能放在 实体类包 model 中,
因为 model 包中都是和数据库一 一对应的,model 包中一个类,代表数据库中一个表,
新建一个类进去,不对应数据库中的任何表
所以为这种类 新建一个包 vo ,就是 运行过程中需要用到的,但是又不和数据库对应的 这种类。 value object 值对象
后面的 注册、问题、评论、回答 都会用到 vo
控制器 接受表单信息
需要 定义一个 实体类 来接收 用户填写的注册信息
User类不能满足表单需要,我们就自己创建一个 vo (Value Object) 类型
RegisterVo 这5个属性,一 一对应注册表单的那5个框(就是register.html页面中name那)
准备好了这个Vo类
下面去编写 控制器,接受表单提交的信息,而这个控制器方法的参数类型 就是上面定义的Vo类型
在 controller 包中新建一个SystemController类,用户处理系统操作 (如 注册、系统相关的、用户管理 ;登录是交给 Spring Security了 )
写代码时 注意:
(1)用 Getmapping 还是 Postmapping 是根据 请求方式决定的,
手输地址 或 链接访问都是get方式,用 Getmapping ,
但这里是 表单提交 ,
而且 上面 register.html 那 method 是post 请求 ,所以用 Postmapping
(2)路径 要和 register.html 中action那一样,所以是 /register
(3)写 RegisterVo,这样的话 register.html 提交的表单 邀请码,电话(用户名),昵称,密码,确认密码 就都会被赋值到 registerVo 对象的属性中,(因为属性名字 和 name那的名字一样),赋值到属性中之后就能获得 用户输入的 表单信息了
(4)这是 log 对象的方法(字符串,参数),参数 registerVo往字符串里的{ }中赋值,{ }是占位符,{ }不会显示到输出内容中
无论是 页面 还是控制器(controller)的 路径, 都要放行,都属于页面的路径,都属于Spring Security保护的范围
注意:
register.html 是用户看到的注册页面 ,需要填信息,点击注册
register 是执行注册业务逻辑的控制层代码
所以 /register 路径 实现注册 也是需要进行放行的,
添加放行路径
重启服务进行注册,检查控制台是否能 输出用户在注册页面填写的信息
如果能够输出表示一切正常
从数据库中 复制一个邀请码
手机号内部有验证,所以格式要正确
以上 证明了 用户输入的表单信息 从页面发送 到控制器了,控制器(controller)接收到了,
下面做 业务逻辑层 代码
中间穿插一个小知识,连接数据库的查询方式(MyBatis Plus 提供的一个小功能)
利用QueryWrapper实现邀请码的验证
学生输入的邀请码 需要 连接数据库 来验证是否正确
之前 是ClassroomMapper 接口中写这个 @Select 查询方法,然后写SQL 语句
现在 MyBatisPlus 还提供了另外一种查询方式
这两种方式都可以
我们先在 测试类 中编写一个示例
创建一个测试类ClassroomTest
自定义异常类型
如果 注册 过程中发生了 邀请码不正确 或 用户名已经被使用 的情况,就是业务逻辑层代码中的异常情况,
我们需要使用Java提供的异常处理机制来处理,发生异常的类型是我们自定义的,所以我们需要定义这个业务异常类
把老师发的 提供代码 ServiceException 粘贴过来,改动 删除
异常处理类准备好了,也学习了新的查询数据的方式 QueryWrapper
下面我们就开始编写注册的业务逻辑代码
编写学生注册的 业务逻辑层(Service)
注意:
企业中开发项目编写业务流程:
最基本的调用格式就是
- 前端发起请求到控制器(controller)
- 控制器接收信息后调用 业务逻辑层(Service)方法
- 业务逻辑层中调用 数据访问层(mapper)方法,并接收 数据访问层 的返回值
- 业务逻辑层 将接收到的返回值返回给 控制层
- 控制层 将信息响应给 前端
之前是 前端到控制器,控制器到数据访问层,数据访问层 返回
现在 多了个业务逻辑层
业务逻辑层 就是把 前面 vrd 项目中 Controller 的一部分代码 和 调用 数据访问层的一部分代码 ,移到了 另外一个单独的类中,这个类 就是业务逻辑层。
现在的控制器(controller) 只负责 和 前端(vue、axios、异步/同步请求)进行信息交互,前端把 用户 信息给控制器,控制器去调用注册方法,最后把注册结果返回给前端,控制器只做这个,怎么注册、怎么验证,控制器一概不管。
·············································································
业务逻辑层的格式 是一个接口对应一个实现类,
已经在代码生成器的生成范围中,无需我们创建
接口命名规范
I+实体+Service 例如 I User Service
实现类命名规范
实体+Service+impl 例如 User Service impl
无论是接口还是实现类
在声明对象名 时,不需要声明接口开头的 I 和 实现类结尾的 impl
例如 声明接口类型对象
IUserService userService
声明实现类类型对象
UserServiceImpl userService
·············································································
在编写任何业务逻辑时,都是先编写接口,在接口中声明方法
在 IUserService 接口中 添加 注册学生 的方法,代码如下
用 void (不用返回值 String),用 异常 表示是否出现问题,如果 出现问题,就抛异常。用返回值就没有意义了
RegisterVo 就是需要注册的用户了,用户信息全在里面
registervo 这个参数,是由 页面传给 controller,控制层controller 接收 并提供给业务逻辑层的;
即 页面传给 controller, 在控制层中会获得 UserService 实体对象, 然后调用 registerStudent() 方法,并把参数传过去
开始编写 实现类 代码
把 光标 放在接口名上,Ctrl + Alt + B ,就能 从接口 自动跳转到 实现类 上
当前 实现类报错, Alt + Enter
就会 生成一个方法
注册的 业务逻辑步骤
UserRoleMapper 数据库那边有下划线,Java 这里把下划线去了,把后面变成大写
验证邀请码正确性 测试类 中写过