SpringSecurity是专门针对基于Spring项目的安全框架,充分利用了依赖注入和AOP来实现安全管控。在很多大型企业级系统中权限是最核心的部分,一个系统的好与坏全都在于权限管控是否灵活,是否颗粒化。在早期的SpringSecurity版本中我们需要大量的xml来进行配置,而基于SpringBoot整合SpringSecurity框架相对而言简直是重生了,简单到不可思议的地步。
SpringSecurity框架有两个概念认证和授权,认证可以访问系统的用户,而授权则是用户可以访问的资源,下面我们来简单讲解下SpringBoot对SpringSecurity安全框架的支持。
本章目标
在SpringBoot项目中使用SpringSecurity安全框架实现用户认证以及授权访问。
构建项目
我们使用IntelliJ IDEA工具创建一个SpringBoot项目,预先加入JPA、Security、Druid、MySQL等依赖,项目结构如下图1所示:
![4461954-dd17db5d253b6727.png](https://i-blog.csdnimg.cn/blog_migrate/5c2f9af17165e083ff3ca91d3dca7c5f.png)
我们下面先来配置数据库访问的配置,将我们之前章节(第十三章:SpringBoot实战SpringDataJPA)的application.yml配置文件复制到本章项目resources目录下,如下图2所示:
![4461954-9ad9764affec52a5.png](https://i-blog.csdnimg.cn/blog_migrate/ffc832aa8e294211f5644324fc4b2169.png)
用户和角色
数据库连接配置完成后,我们开始创建本章需要用到的三张表,用户表、角色表、用户角色关联表,一个用户存在多个角色!用户表结构图下图3所示:
![4461954-7a92c3926d10d06d.png](https://i-blog.csdnimg.cn/blog_migrate/9f5e7b55214d571dab89816d74068ee6.png)
我们用户表结构仅有三个字段,这里只是为了演示我们的安全框架,所以不做太过详细。下面是我们的角色信息表结构如下图4所示:
![4461954-f7a128157fab701e.png](https://i-blog.csdnimg.cn/blog_migrate/5a6de8917e270c83ae1cc59267fbd00b.png)
因为我们一个用户存在多个角色,一个角色又可以应用到多个用户上,所以我们采用的关联表的方式进行配置关系,用户角色关联表结构如下图5所示:
![4461954-dd563d02c38a6717.png](https://i-blog.csdnimg.cn/blog_migrate/7c594ba3bb421dc00a27689093f5cbda.png)
下面我们根据用户信息表以及角色信息表创建对应的实体,如下图6、图7所示:
![4461954-a65b72c9951c6de2.png](https://i-blog.csdnimg.cn/blog_migrate/91925c8356820c47b78dc4ee195f17ae.png)
可以看到我们的UserEntity实现了UserDetails接口,UserDetails是SpringSecurity验证框架内部提供的用户验证接口(我们下面需要用到UserEntity来完成自定义用户认证功能),我们需要实现getAuthorities方法内容,将我们定义的角色列表添加到授权的列表内。
![4461954-eb61ad05cadfa7c4.png](https://i-blog.csdnimg.cn/blog_migrate/16d30975c9b53bafcef335249c42c3a8.png)
可以看到我们的用户实体内添加了对角色的列表支持,并添加了@ManyToMany的关系注解。我们查询用户时SpringDataJPA会自动查询处关联表user_roles对应用户的角色列表放置到名叫roles的List集合内。
填充测试数据
我们对用户表、角色表、关联表添加几条对应的数据,SQL脚本如下图8所示:
![4461954-467e3af745e0b7df.png](https://i-blog.csdnimg.cn/blog_migrate/ece11431a4f6f523e3f0618b733fa58b.png)
初始化的SQL脚本已经添加到本章的resources目录下,本章结束为止会有源码下载地址。
配置JPA访问数据
根据创建的UserEntity实体来创建UserJPA接口并继承JPARepository接口,UserJPA内添加一个根据用户名查询的方法,如下图9所示:
![4461954-4c01cebdf6ac263f.png](https://i-blog.csdnimg.cn/blog_migrate/0a35fe907a00ebe9bd829f9e859b47a4.png)
我们可以看到我在图9的UserJPA接口内添加了一个findByUsername方法,这个方法其实是SpringDataJPA的一个规则,我们这样写JPA就会认为我们要根据username这个字段去查询,并自动使用参数索引为0的值(有关SpringDataJPA方法查询后期会在SpringDataJPA 核心技术内体现)。
自定义SpringSecurity用户认证
我们上面的配置差不多已经完成,下面我们实现SpringSecurity内的UserDetailsService接口来完成自定义查询用户的逻辑,如下图10所示:
![4461954-c13b7898fe682985.png](https://i-blog.csdnimg.cn/blog_migrate/9599839e8799cc461fe1ee30808116a7.png)
可以看到上图10内的定义,实现UserDetailsService接口需要完成loanUserByUsername重写,我们使用UserJPA内的findByUsername方法从数据库中读取用户,并将用户作为方法的返回值。
配置SpringSecurity
自定义用户认证已经编写完成,下面我们需要配置SpringBoot项目支持SpringSecurity安全框架,具体配置代码如下图11所示:
![4461954-79c80a0b3c9b732a.png](https://i-blog.csdnimg.cn/blog_migrate/14f588716543d12110e8d56033ac52d5.png)
可以看到我们上图11配置了所有请求都必须登录访问,第一句我们仅用了csrd,在springSecurity4.0后,默认开启了CSRD拦截,如果需要配置请在form表单添加如下图12配置:
![4461954-8fa1e754f807aae6.png](https://i-blog.csdnimg.cn/blog_migrate/f3f24ae1e8922f12c49c43bcf62a707b.png)
我们这里配置了登录页面127.0.0.1:8080/login请求地址以及登录错误页面/login?error不被SpringSecurity拦截。下面我们来编写登录的JSP页面,我们之前构建项目的时候并没有添加JSP的依赖,下面我们修改pom.xml添加JSP依赖,如下图13所示:
![4461954-7ad77c2ff7ad8e9c.png](https://i-blog.csdnimg.cn/blog_migrate/ca4f64e9db15a6cc8ce59af6c3865b1e.png)
我们修改application.yml配置文件添加JSP的页面配置,如下图14所示:
![4461954-9e32b801bd496598.png](https://i-blog.csdnimg.cn/blog_migrate/07d7058128d85afc2db979ebe1c4af46.png)
好了,下面我们简单的创建一个login.jsp页面,页面里面添加一个简单的表单提交,我们的表单提交地址这里要注意了,SpringSecurity内部已经给我们定义好了,在4.0版本之后登录地址都是/login,当然这个/login并不是我们上面配置的loginPage地址。这个地址如果直接访问是访问不到的。必须采用Post形式访问,login.jsp页面内容如下图15所示:
![4461954-8eb6bc39b1b8cd59.png](https://i-blog.csdnimg.cn/blog_migrate/a304cd03da19a99fb90847b865b09246.png)
我们来配置一个简单的SpringBoot内的MVC控制器跳转,下面我们添加一个名叫MVCConfig配置类继承WebMvcConfigurerAdapter类,重写addViewControllers()方法添加路径访问,可以通过Get形式的/login访问到我们的login.jsp,代码如下图16所示:
![4461954-e058063b8ecc6a62.png](https://i-blog.csdnimg.cn/blog_migrate/dd0df20406088dcac078d3f768cad211.png)
上面有关SpringSecurity配置都已经完成,接下来我们添加一个IndexController来测试我们的SprinySecurity框架是否已经生效,我们上面已经配置了,如果在不登陆的状态下只有/login是可以访问的,所以我们直接访问/index是不可行的。SpringSecurity会直接给我们重定向到我们配置的loginPage,IndexController代码如下图17所示:
![4461954-6ee31369187ec28f.png](https://i-blog.csdnimg.cn/blog_migrate/767c1d520468739040fb3a039b596732.png)
运行测试
下面我们来启动项目,先来访问/index查看界面效果,如下图18所示:
![4461954-cb01310ecb08f41a.png](https://i-blog.csdnimg.cn/blog_migrate/cc7bb1fe9c2666c74e6f51c8a75f8e55.png)
正如我们所说的,当我们在没有登录的状态下访问/index时,会直接被安全框架重定向到登录页面,那么我们登录后,再来访问/index并查看界面输出,如下图19所示:
![4461954-6311fed3b19d21fb.png](https://i-blog.csdnimg.cn/blog_migrate/dedd7053482285151742ec3ca1db3f49.png)
可以看到界面的效果,我们已经可以正确的访问到index路径所返回的数据,证明了我们的安全框架已经生效了。
角色判断
我们在文章开始的部分已经创建了角色表,我们下面就要根据角色,在用户登录成功后显示不同的内容,在这之前我们需要添加SpringSecurity为我们提供的JSTL标签库,我们可以根据标签库自行判断登录用户的角色,我们修改pom.xml配置文件添加如下图20所示依赖:
![4461954-7501f3703ca7e36d.png](https://i-blog.csdnimg.cn/blog_migrate/420559de6ba9ba8628ad8272bbb62e2e.png)
下面我们创建main.jsp,在jsp页面引入SpringSecurity标签库,并根据角色判断输出内容,如下图21所示:
![4461954-350f466ad77802f6.png](https://i-blog.csdnimg.cn/blog_migrate/2870d11e8895b300e7f42516e4a6487a.png)
我们在main.jsp判断了是超级管理员、普通用户的角色并根据不同的角色输出不同的内容,下面我们重启项目访问127.0.0.1:8080/main地址查看界面输出内容,如下图22所示:
![4461954-37e3aeff9c95b7e7.png](https://i-blog.csdnimg.cn/blog_migrate/2b1d623a48094e430fdf8382bf523921.png)
我们的界面并没有输出任何的内容,这是为什么呢?
SpringSecurity不支持中文比对,所以我们这里不能直接使用角色中文名称作为判断条件,下面我们修改roles信息表添加一个标识字段,如下图23所示:
![4461954-2138e1a56d15eea9.png](https://i-blog.csdnimg.cn/blog_migrate/59f6ac942b7453f3eb57c025e18e77ec.png)
下面我们对应的添加RoleEntity实体内字段,如下图24所示:
![4461954-63d54025478c2d9b.png](https://i-blog.csdnimg.cn/blog_migrate/961649b7812bd5b82b4d44e1e968b279.png)
还有最重要的一点,我们的UserEntity内是根据RoleName来添加角色授权的,下面我们需要改成flag,如下图25所示:
![4461954-779d863af0d703e1.png](https://i-blog.csdnimg.cn/blog_migrate/33663a4b99c089ad2ed5e41c1c1407fd.png)
最后我们修改main.jsp,hasRole改成对应的flag字段值,如下图26所示:
![4461954-ad5aa3e9261cfe4e.png](https://i-blog.csdnimg.cn/blog_migrate/3a4d46fdf4cbe1f145b8ba219a0446aa.png)
上图的ROLE_USER、ROLE_ADMIN是我们在roles表内的对应添加的数据,也是作为授权的flag字段值,下面我们重启项目,登录后再来访问127.0.0.1:8080/main页面查看界面输出如下图27所示:
![4461954-7962eb80cfa69bd1.png](https://i-blog.csdnimg.cn/blog_migrate/65c56ef770c3fae2c499f3a68d284fd0.png)
界面为了们输出了两条信息,这个是正确的,因为我们在user_roles表内为admin用户添加了两条对应的角色。下面我们修改user_roles表结构,删除对应普通用户的关联,再来访问127.0.0.1:8080/main,界面输出效果如下图28所示:
![4461954-778522dabea54027.png](https://i-blog.csdnimg.cn/blog_migrate/4742777dd36f24c8bd8e42030b55eeb0.png)
我们很奇怪,数据为什么没有变化呢?因为SpringSecurity将我们的用户数据、角色数据都缓存到框架内,下面我们来重启下项目再次访问后,界面输出内容如下图29所示:
![4461954-8cb697c6130b828c.png](https://i-blog.csdnimg.cn/blog_migrate/31595bcfbe5a3ef56057c7ff13dead14.png)
这次界面输出的内容才是正确的。
总结
以上内容就是本章的全部内容,本章主要讲解了SpringBoot项目中如何使用SpringSecurity来作为安全框架,并通过SpringSecurity提供的JSTL标签库来判断界面的输出,还有如果修改了用户的权限不会实时生效,而需要我们退出用户后再次登录方可生效。
本章的代码以及SQL都已上传到码云:
SpringBoot配套源码地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源码地址:https://gitee.com/hengboy/spring-cloud-chapter
SpringBoot相关系列文章请访问:目录:SpringBoot学习目录
QueryDSL相关系列文章请访问:QueryDSL通用查询框架学习目录
SpringDataJPA相关系列文章请访问:目录:SpringDataJPA学习目录
SpringBoot相关文章请访问:目录:SpringBoot学习目录,感谢阅读!
欢迎微信扫码加入知识星球,恒宇少年带你走以后的技术道路!!!
![4461954-07a5282d8233745f.JPG](https://i-blog.csdnimg.cn/blog_migrate/117a071f8b87a8af29272eabd77dab12.jpeg)
知识星球 - 恒宇少年