基本上每一个项目都会有用户登录的这个功能,用户需要在登录之后才能够去访问一些资源,如果没登录的话就不能访问(403)。我们可以自己编码去实现这样的业务逻辑,当然每一次都自己去编码是比较耗时的,毕竟市面上已经有现成的开源的框架可以拿来使用了(Apache的shiro与Spring的Spring Security)
,这一篇的博客的主角就是介绍一下后者在SpringBoot中的整合配置,以及如何基本使用
首先还是一样,要用到什么技术,就引入什么技术的maven依赖:
<!-- 引入security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
同时因为后面还要用到thymeleaf
模版引擎,所以我们顺便把thymeleaf
也导入了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
像shiro、Springsecurity
这些框架技术都统称为安全框架,安全框架有两个重要的作用,是认证和授权。认证是程序确认你是什么身份,而授权就是程序根据你的身份给你不同的权利。
这里不讲空话,用我看的学习资源里面的一个场景,一个武林秘籍系统,用户可以根据不同的权限从而查看到不同的武林秘籍,这是初始的样子:
我们现在需要做的是点进去每个秘籍,需要不同的身份,比如我是普通的用户,那么我只能去查看普通的武林秘籍,同理,另外两个也是一样,而且,当我们登录之后会去显示出用户的信息和所拥有的身份。
这时就可以用到springsecurity
了,我们需要创建一个SpringSecurity的配置类,让其继承于WebSecurityConfigurerAdapter
,并实现一些方法:
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@EnableWebSecurity
注解的作用是开启security的服务(有点废话),它内部也是个组合注解,有一个@Configuration
,可以理解为通知SpringBoot让其扫描到该类。
之后看看让其继承两个方法,为什么是两个呢,一个是认证,另外一个是授权啦:
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("VIP1") //普通武林秘籍,并设置所需身份为VIP1
.antMatchers("/level2/**").hasRole("VIP2") //高级武功秘籍,并设置所需身份为VIP2
.antMatchers("/level3/**").hasRole("VIP3"); //爵士武功秘籍,并设置所需身份为VIP3
//自动配置登录,使用这行代码之后,springsecurity会帮我们生成一个登录,并自动帮我们校验用户名和密码!,默认的路径为/login
http.formLogin();
//自动配置注销,可以通过访问路径/logout,来帮我们注销当前用户,底层原理是撤销相关的session
http.logout().logoutSuccessUrl("/");
//自动配置记住我功能,就像是很多网站的"记住我"功能,使用这个后,下次进入网站无需登录,底层原理是像浏览器发送一个cookie信息,内容是一个sessionid,有效期14天,这样下次访问时带上这个sessionid就能找到session了
http.rememberMe();
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这里为了简便,就不从数据库中换取数据了,从内存中获取,inMemoryAuthentication()就是从内存中获取,这里分别设置了两个用户,它们的身份分别是"VIP1"、"VIP2"和"VIP3",用户名和密码见具体方法。
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()).withUser("leslie").password("123").roles("VIP1","VIP2")
.and()
.withUser("lion").password("123").roles("VIP3");
}
每一行代码的作用写在注释中。总结下,到这一步,我们做完的事情有给leslie
和lion
的两个用户授权VIP1、VIP2和VIP3的身份,并为不同的访问路径授权了不同的用户。
配置类写完之后,还不够,这里controller
代码略过了,无非就是控制页面跳转到哪里。前端页面代码也是需要修改了,修改之前需要引入thymeleaf-springsecurity
的整合:
<!-- 引入thymeleaf与spring security整合-->
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
修改后的前端代码:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 align="center">欢迎光临武林秘籍管理系统</h1>
<div sec:authorize="!isAuthenticated()">
<h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/login}">请登录</a></h2>
</div>
<div sec:authorize="isAuthenticated()">
<h2 align="center"><span sec:authentication="name"></span>您好,你的角色是:<span sec:authentication="principal.authorities"></span></h2>
<form method="post" th:action="@{/logout}">
<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:authorize="hasRole('VIP2')">
<h3>高级武功秘籍</h3>
<ul>
<li><a th:href="@{/level2/1}">太极拳</a></li>
<li><a th:href="@{/level2/2}">七伤拳</a></li>
<li><a th:href="@{/level2/3}">梯云纵</a></li>
</ul>
</div>
<div sec:authorize="hasRole('VIP3')">
<h3>绝世武功秘籍</h3>
<ul>
<li><a th:href="@{/level3/1}">葵花宝典</a></li>
<li><a th:href="@{/level3/2}">龟派气功</a></li>
<li><a th:href="@{/level3/3}">独孤九剑</a></li>
</ul>
</div>
</body>
</html>
这里说明一下,添加一些代码的作用
页面首先需要引入thymeleaf
的约束空间:
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
引入之后就可以使用一些和springsecurity
有关的标签了。
- sec:authentication=“name”:表示取出当前登录的用户名。
- sec:authentication=“principal.authorities”:取出当前用户的所有身份
- sec:authorize=“isAuthenticated()”:判断当前状态是否登录,同理属性内前面加一个!表示判断是否未登录,有这个之后,我们以后就不需要自己编写拦截器判断判断登录状态了。
- sec:authorize=“hasRole(‘VIP3’)”:判断当前登录用户是否有该身份。
之后我们再次查看该页面:
可以看到 sec:authorize=“isAuthenticated()” 的作用出来了,如果未登录的话,只显示这个,点击登录,看看Springsecurity为我们生成的登录页面:
还挺好看的,下面的那个单选框"Remember me on this computer"是之前http.rememberMe();
所自动配置的功能。首先登录leslie
,看看能否显示前两个级别的武功秘籍:
成功,点击注销(刚刚http.logout()
的作用),再换成lion
用户试试,看看能否显示绝世武功秘籍:
简单记录到这里,由于springsecurity
的具体知识我也还没有学过,觉得挺方便的,之后找机会补完这方面的知识,再着重记录。