SpringBoot基于SpringSecurity表单登录和权限验证的示例
作者:润青 发布时间:2022-05-05 13:28:49
一、简介
上篇介绍了一个自己做的管理系统,最近空闲的时间自己在继续做,把之前登录时候自定义的 * 过滤器换成了基于SpringSecurity来做,其中遇到了很多坑,总结下,大家有遇到类似问题的话就当是为大家闭坑吧。
二、项目实现功能和成果展示
首先来看下登录界面:这是我输入的一个正确的信息,点击登录后SpringSecurity会根据你输入的用户名和密码去验证是否正确,如果正确的话就去你定义的页面,我这里定义的是查询教师信息页面。来看下代码吧。
三、准备工作(前台页面、实体类)
实体类Teacher:字段主要有id、name、sex、email、schedule_Id、password、phone,上面都加了Hibernate的验证,其他字段大家可以自己写,然后生成getter和setter方法
再来看下前台页面,前台我使用的是一个BootStrap的页面,模板语言使用的是Thymeleaf,当然为了能够在页面支持security的语法需要在上面加入一个security的引入标签
index.html"页面
index.html中登录信息:需要注意的是我的表单是提交到/teacher/login这个路由上,然后就是字段中的name属性必须要和你Teacher实体类中的字段名一致,不然页面数据传不到后台。这里只截图2个字段吧太长了其他的字段都一样该好名字就行
然后我们再来看下TeacherController中的代码:
这里面的代码是我之前没使用SpringSecurity的时候写的,现在注释掉了,当我们登录成功之后就会到/query这个路由下的方法去执行,也就是执行查询教师信息,先不看query方法,我们现在先来对接SpringSecurity,让它先帮我们来进行index中用户名和密码的登录验证,然后再看query方法
四、使用SpringSecurity进行表单验证登录
要想使用SpringSecurity就需要现在pom.xml中加入SpringSecurity的依赖,你可以指定版本号也可以不指定,我这里没有指定
注意:当你引入SpringSecurity之后当你再次去启动项目的时候,SpringSecurity自动会给你跳到一个对话框,让你输入账号和密码,这里的用户名是user,密码在你启动的时候它会有一个加密的密文,你只需要复制进去就可以登录。
接下来我们要想实现自定义的表单验证登录和其他高级功能就需要使用配置类来配置,在SpringBoot中新建一个配置类,让它来继承WebSecurityConfigurerAdapter,然后重写里面的configure方法,在里面定义我们的逻辑,来看下代码吧:
上面的是我项目中的配置,有些是本文用不到的,分别解释下:从开始来说,http.formLogin的作用是使用form表单进行登录,也就是我们的index页面。当然你也可以使用Batic登录,就是之前说的默认给你的登录信息界面。
上面的URL是我们的index页面的数据也就是登录的字段数据,当时我们提交的路径是/teacher/login,而我们这里的配置意味着让SpringSecurity去处理我们index表单数据,你这样写SpringSecurity就知道对这个路径进行登录验证处理。
请注意下面的代码因为有坑:
可以看到我这边配置了2个Mathchers,如果不配置的话会出现什么状况呢?你可能会遇到下面的这种错误
什么意思呢?我先来简化出来一个简单的配置,下面的这段配置的意思:处理登录的Url为/teacher/login,对任何的请求都进行验证,下面的csrf先忽略,那么上面的定向次数太多是怎么来的呢?当你点击登录之后,SpringSecurity会去处理/teacher/login这个请求,然后它发现这个请求也是需要验证的,于是乎就进入了死循环,它一直在验证。。。
要想解决这个问题,其实也很简单,就是给它弄一个匹配器,告诉SpringSecurity,我在访问这个路径的时候你要放行不要拦截,比如说你可以这么写:.antMatchers("/teacher/login").permitAll(),有的朋友可能看了我上面的代码,你并不是这么写的啊,你没有对/teacher/login放行啊!还记得我上面说的吗在们登录成功之后也就是/teacher/login验证之后是要去执行/query方法去查询教师信息的,因此我的匹配器匹配的是/query,所以我这么写.antMatchers("/query").hasAnyRole("admin","stu")的时候并没有permitAll,是因为后面的hasRole,表示看你当前登录的用户有没有admin或者是stu这样的角色,有的话我就放行没有的话就不放行,有的朋友可能会说那怎么角色该在哪里定义呢?下面我来看一下
五、角色匹配
这个时候我们需要重写Adapter的另外一个configure方法注意这个方法的参数类型是AuthenticationManagerBuilder,它是一个认证管理构建器,可以帮我们构建出你要对什么东西进行认证,比如角色、用户、密码,我们先来写一个内存认证,后台会说怎么连数据库去认证权限
上面这段代码的意思是:我的认证用户为安安,密码为:123123,安安的角色是admin,只有这些信息正确之后我才让你认证成功,去执行下面的逻辑。我们来看下演示:
这就是代表登录成功了,至于上面的角色显示什么的我都会说到,那么我们来换一个,刚才/query中的hasRole里面是admin和stu,我把admin去掉看下能登录成功吗?需要有一个stu的角色才能在登录成功之后查看信息,而我们现在的用户名和密码都正确的情况下,角色不正确能够成功吗?
可以看到即使是使用了上面正确的用户名和密码登录,但是提示没有权限,这个页面是我自定义的错误页面,你的可能会提示403没有这些样式。
六、从数据库中读取角色来验证
在真实的开发中,我们肯定是不会将用户信息这种东西放到内存中去的,肯定时从数据库中读取的,我在数据库中新建了一张表,教师角色表,我们就从这张表读取角色信息然后交给SpringSecurity去判断角色
实体类这里就不截图了,然后我们在TeacherRoleRepository中有这么一个方法,就是根据TeacherId去查询教师的角色,为了方便我这里就不去新建什么Service了,直接就TeacherRoleRepository调用里面的findByTeacherId()方法
接下来,我们编写一个TeacherDetailsService它实现一个UserDetailsService接口,UserDetailsService里面只有一个方法就是loadUserByUsername(String username),这么方法是干嘛的呢,是根据你提供的用户名它去查出用户的具体信息,然后返回一个User对象(SpringSecurity中的User对象),这个User对象会携带着你需要验证的信息去验证,如果通过的话就进行放行,那么User对象都会有哪些参数呢?来看下源码:你可以只传这三个参数,当然你也可以传剩下的那些,比如密码是否被锁定,账户是否被冻结,是否传这些看你业务需要的,当然你可以自定义一个类,然后让它去实现UserDetails,实现里面的这些方法,因为这7个方法都是UserDetails接口里的。
在我的项目里没有重写,直接携带着用户信息返回了User对象,需要的三个参数中第一个参数username已经有了,是SpringSecurity为我们获取的,至于怎么获取的,我们在最后的文章中说原理的时候再谈,然后就是password,有了username的话拿到password就很简单,我们自定义了一个TeacherRepository类,里面有一个根据name获取信息的方法,然后直接获取密码就好,最关键的是第三个参数,这个参数的意思是让我们传入授权,在我们这里就是角色,首先第一步我们先根据教师id获取到角色信息,这个不难,关键是角色信息是一个List,因为一个人可能有多个角色,意味着我们可以很简单的拿到一个角色列表,然后我们需要用到一个类,这个类是GrantedAuthority它表示已经被授权的权限,我们来构建一个GrantedAuthority类型的数组,用来表示已经被授权的角色,然后我们来实例化一个它的实现类SimpleGrantedAuthority这个类会接受一个String类型的role,然后去进行授权。
什么意思呢?大概的流程是这样的,首先我们从数据库中读取到了这个用户对应的角色,它是一个列表,记着我们要把这个列表中的值传给SpringSecurity去判断,看我这个列表里面有没有你需要认证的角色,如SpringSecurity需要一个admin角色才可以访问/query。这时数据库中这个用户有一个角色为admin,那么就让它访问/query,GrantedAuthority、SimpleGrantedAuthority它们可以简单理解为是用来帮你把数据库中的角色交给SpringSecurity来验证。
代码如下:
然后在我们之前的配置类中的configure方法就要改一下,因为我们之前是从内存中读取的,现在是从数据库中读取出来的
我们需要验证的信息都在TeacherDetailsService中,因此我们还需要实例化一个TeacherDetailsService
细心的朋友可能会看到auth中还有一个passwordEncoder,这个下篇里面说,如果在SpringSecurity中使用自定义数据库的加密方式,需要注意的是我们这样写完之后会报错,当时我也很懵,后来查阅了好多资料才发现的,角色认证的一个大坑!!
经过上面的信息认证之后SpringSecurity发现我们输入的信息是对的,但是再次登录后还是会报错403,告诉你没有认证成功,没有对应的权限,这是为什么?后来查阅了SpringSecurity的API发现有这么一段
就是说当你从数据库中查到角色之后,虽然SimpleGrantedAuthority接收的是一个字符串角色,但是最终你返回的User对象中Collection<? extends GrantedAuthority> authorities这里需要的格式是ROLE_A这种格式,所有你需要在传给User对象之前,将角色名称前加一个ROLE_,比如我下面的这样:RoleName就是我定义的ROLE_字符串,这样才能够被SpringSecurity所认证。
之前看有的文章说是因为hasRole需要在前面加ROLE_才可以,所以在配置文件中试了一下ROLE_ADMIN不好使,而且查阅官方API发现hashRole和hashAnyRole都不需要前面写ROLE_
原来之所以不用写是以为在SimpleGrantedAuthority传入之后的格式中有了ROLE_限定
来源:https://blog.csdn.net/qq_32967665/article/details/86296980


猜你喜欢
- Java 8 中 Function 接口的介绍Java 8 中提供了一个函数式接口 Function,这个接口表示对一个参数做一些
- 本文实例讲述了Android开发实现模仿微信小窗口功能。分享给大家供大家参考,具体如下:运用方法:将显示窗口的风格 设置为对话框风格即可具体
- 最近在研究android自定义控件属性,学到了TypedArray以及attrs。大家也可以结合《理解Android中的自定义属性》这篇文章
- 本文实例讲述了Android编程实现图片的浏览、缩放、拖动和自动居中效果的方法。分享给大家供大家参考,具体如下:Touch.java/**
- 什么是异常?最简单的,看一个代码示例:public static void main(String[] args) { &nb
- 首先来说一下本文中例子所要实现的功能:基于ProtoBuf序列化对象使用Socket实现时时通信数据包的编码和解码下面来看具体的步骤:一、U
- 先上效果图这个效果来自于三星S5的充电界面,当然有些细节差别,主要看思路.本文目的是技术交流,不要将效果直接运用于商业产品和项目.电池背景因
- 背景大家在使用Selenium + Chromedriver爬取网站信息的时候,以为这样就能做到不被网站的反爬虫机制发现。但是实际上很多参数
- 一、介绍1.两种方式,一种使用tomcat的websocket实现,一种使用spring的websocket2.tomcat的方式需要tom
- 在本文中,我们将看到几个关于如何在Java 8中对List进行排序的示例。1.按字母顺序排序字符串列表List<String>
- Android ViewGroup中的Scroller与computeScroll的有什么关系?答:没有直接的关系知道了答案,是不是意味着下
- 里氏替换原则(LSP)定义:在任何父类出现的地方都可以用它的子类类替换,且不影响功能。解释说明:其实LSP是对开闭原则的一个扩展,在OO思想
- 最近要做动态数据的提交处理,即需要分析提交数据字段定义信息后才能明确对应的具体字段类型,进而做数据类型转换和字段有效性校验,然后做业务处理后
- 本文基于ThreadLocal原理,实现了一个上下文状态管理组件Scope,通过开启一个自定义的Scope,在Scope范围内,可以通过Sc
- 动态SQL实现前端指定返回字段问题描述在使用ClickHouse时,遇到需要根据业务需求,动态返回指定字段,从而充分利用ClickHouse
- 本文实例讲述了c#用for语句输出一个三角形的方法。分享给大家供大家参考。具体分析如下:这是一道面试题,要求是这样的:只使用一个for循环输
- 1.定义多态是同一个行为具有多个不同表现形式或形态的能力。多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口
- 在Java中我们知道静态变量会在类加载时机的“初始化”阶段得到赋值(编译器会收集类中的静态变量及静态
- 本文实例讲述了C#实现生成所有不重复的组合功能。分享给大家供大家参考,具体如下:给你几个字母,比如(a,b,c,d,e,f),要求生成所有不
- 我们有时候会遇到这样的情况,需要获取某些中文的拼音、中文首字母缩写和中文首字母,下面我将为大家介绍一下如何获取中文拼音的缩写。1、项目建立和