没错,又是spring boot2.0和1.0之间版本不同的问题!互联网的东西更新的是真的快,很多东西才过了一段时间,用法就不一样了,最近在学习Spring Security这个安全框架的时候又遇到了,很苦逼。
首先,先简单介绍一下(很简单的介绍,想要详细了解这个框架的话,还是去看官方文档或者网上的中文文档,里面都有详细介绍)
Spring Secutity:一个能够为基于Spring的企业应用系统提供声明式的安全訪问控制解决方式的安全框架(简单说是对访问权限进行控制嘛),应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
下面我们来详细了解一下 Spring Security 的用户认证和用户授权功能。
一、搭建 Spring Secutity 环境(spring-boot 2.1.6,spring-security-xxx 5.1.5.RELEASE,都是maven下载最新版本)
1、使用IDEA(旗舰版)快速生成 Secutity环境,并且导入 thymeleaf 模板引擎,方便后面步骤的展开
小提示:IDEA大家最好都去用旗舰版,虽然需要破解,可能费点功夫,但是功能比社区版多很多,笔者亲身使用过社区版,为了跟上旗舰版的功能,装了很多插件,有时候都装不上,破解的方法网上百度一大把,笔者这里就不过多描述了。
笔者这里的环境配置基本是最新版本,也是因为最新版本,所以会有很多新的东西,新的语法,学习过程中可能会有很多问题,但是由于自己比较喜欢学新的东西,看文档,所以一直坚持着用最新版本的东西来学习。
二、编写用户登录过滤器
核心:(1)@EnableWebSercurity 注解用于开启 Spring Security,禁用Boot的默认Security配置,配合@Configuration启用自定义配置;
(2)继承 WebSecurityConfigurerAdapter 构造器类,它提供了(http,web,auth)三个configure方法,用于我 们下面用户登录授权等操作;
(3)开启spring sercurity自动配置的登录功能:http.formLogin( ) 和自动配置的注销功能 http.logout( )。
@EnableWebSecurity // 启动 Spring security
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
// 上面注释掉父类的方法
// 下面自己定制请求的授权规则
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("VIP1")
.antMatchers("/level2/**").hasRole("VIP2")
.antMatchers("/level3/**").hasRole("VIP3");
// 设置对应的url是全部放行还是要特定的角色才能进入
// 开启自动配置的登录功能,效果:如果没有登录,没有权限就会来到登录页面
http.formLogin();
// 开启自动配置的注销功能
http.logout().logoutSuccessUrl("/"); // 自定义注销成功后去哪个页面
// 1、访问 /logout 表示用户注销,清空session
// 2、注销成功后会返回 /login?logout 页面
}
三、编写用户授权认证规则
因为Spring security 5.0中新增了多种加密方式,也改变了密码的格式;现如今Spring Security中密码的存储格式是“{id}…………”。前面的id是加密方式,id可以是bcrypt、sha256等,后面跟着的是加密后的密码; 也就是说,程序拿到传过来的密码的时候,会首先查找被“{”和“}”包括起来的id,来确定后面的密码是被怎么样加密的,如果找不到就认为id是null;
笔者的解决办法是在inMemoryAuthentication()方法的后面加上:passwordEncoder(new BCryptPasswordEncoder()) 如下
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// 定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// super.configure(auth);
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP2")
.and()
.withUser("lisi").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP2", "VIP3")
.and()
.withUser("wangwu").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP3");
}
如果你用的是在数据库中存储用户名和密码,那么一般是要在用户注册时就使用BCrypt编码将用户密码加密处理后存储在数据库中。并且修改configure()方法,加入".passwordEncoder(new BCryptPasswordEncoder())",保证用户登录时使用bcrypt对密码进行处理再与数据库中的密码比对。
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder())
四、Spring Security5 框架和 thymeleaf 模板引擎相结合,完成最终的用户验证和用户权限控制
1、引入 thymeleaf 和 spring secutrity 的整合包(很重要,笔者就是疏忽,导致卡壳)
<!-- 一定要导入这个thymeleaf和springsecurity5整合的依赖包!!! -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
2、在要构建用户权限控制的html页面上方引入:
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
模板框架,这里需要注意的一个点是,我的 security 是5.0的版本,所以 thymeleaf-extras-springsecurity5 后缀这里的版本号应为 5 ,网上很多文章都说让我们降低版本,改成4,其实也可以,但是相应 pom.xml 文件里面也要对版本进行更改,笔者这里因为不服输,就是要用新版本,所以才会弄了很长时间。
3、使用 sec: 标签进行权限控制
一开始笔者是不会用 sec:标签的,于是上去了spring security的官方文档上面去看,选择自己的版本
然后使用了 <sec:authorize> 这样的写法,发现并不行,又进入卡壳状态,最终经过CSDN某位大佬的文章解救,得出以下结果:完美实现了用户权限的控制。
<h1 align="center">欢迎光临武林秘籍管理系统</h1>
<div sec:authorize="isAnonymous()"> <!-- 如果当前主体是匿名用户,则返回true -->
<h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/login}">请登录</a></h2>
</div>
<div sec:authorize="isAuthenticated()"> <!-- 如果用户不是匿名用户,则返回true,也就是登录状态-->
<h2><span sec:authentication="principal.username" />,您好,您的权限为有:
<span sec:authentication="principal.authorities"/></h2>
<form th:action="@{/logout}" method="post"><!-- 因为要退出,默认方法要是post -->
<input type="submit" value="注销"/>
</form>
</div>
<hr>
<div sec:authorize="hasRole('VIP1')">
<h3>普通武功秘籍</h3>
<ul>
<li><a th:href="@{/level1/1}">罗汉拳</a></li>
<li><a th:href="@{/level1/2}">武当长拳</a></li>
<li><a th:href="@{/level1/3}">全真剑法</a></li>
</ul>
</div>
网上很多存在着 <div sec:authoize access="isAnonymous()"> 再标签里加上 access 属性的写法,笔者在当前的版本下试验过了,并不行,所以请各位读者一定要注意版本问题,毕竟一代版本一代神,很多东西都不一样;好了今天就到这里,下次再会。