入门学习SpringSecurity,这一篇就够了
- 1.SpringSecurity环境搭建
- 2.认识SpringSecurity
- 3.配置SpringSecurity
- 4.注销
- 5.权限控制展示内容
- 6.记住我实现
- 7.定制登录页
在 Web 开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。因此,从应用开发的第一天就应该把安全相关的因素考虑进来,并在整个应用的开发过程中。
市面上存在比较有名的:Shiro,Spring Security
!
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。
Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求
1.SpringSecurity环境搭建
新建一个SpringBoot项目,选用Web API,Thymeleaf API
之后导入前端模板:
前端模板
实现路由控制器跳转:
/**
* 路由跳转控制器
*/
@Controller
public class RouterController {
@RequestMapping({"/", "/index"})
public String index() {
return "index";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") int id) {
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") int id) {
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") int id) {
return "views/level3/" + id;
}
}
引入 Spring Security 模块:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
引入 Spring Security 模块后,再次启动项目,就会提示我们输出用户名和密码了:
这个用户名是user,密码会生成在控制台,每次都不一样的:
登录进去,就能正常跳转到首页了:
2.认识SpringSecurity
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
WebSecurityConfigurerAdapter
:自定义Security策略AuthenticationManagerBuilder
:自定义认证策略@EnableWebSecurity
:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
“认证”(Authentication)🙌
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
“授权” (Authorization)🍟
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
3.配置SpringSecurity
编写基础配置类:
@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
定制请求的授权规则:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权部分
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 定制请求的授权规则
// 首页所有人可以访问
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}
}
此时继续运行项目访问,首页可以正常访问,但是访问其他权限页面出现403错误!成功进行了权限拦截!
但此时自动配置的登录功能消失了,不用担心,在configure()
方法中加入以下配置,开启自动配置的登录功能!
// 开启自动配置的登录功能
http.formLogin();
测试一下:发现,没有权限的时候,会跳转到登录的页面!
为了获得权限查看不同等级的页面,我们可以定义认证规则,重写configure(AuthenticationManagerBuilder auth)
方法
/**
* 认证部分
* 在内存中定义,也可以在jdbc中去拿....
* Spring security 5.0中新增了多种加密方式,也改变了密码的格式
* 要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
* spring security 官方推荐的是使用bcrypt加密方式
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("dahezhiquan").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2", "vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vip1", "vip2", "vip3")
.and()
.withUser("pipi").password(new BCryptPasswordEncoder().encode("pipi")).roles("vip1");
}
根据如上的认证方式,此时我们登录pipi用户,访问level1页面正常,访问其他页面提示403权限不足!
4.注销
开启自动配置的注销的功能:
// 开启注销功能
http.logout();
我们在前端,增加一个注销的按钮,index.html 导航栏中:
<!--注销-->
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
假设,我们想让他注销成功后,依旧可以跳转到首页,可以这样定义:
http.logout().logoutSuccessUrl("/");
之后重启项目,登录成功后,在页面上点击注销:
提示我们是否要注销:点击Log Out!
注销完毕后,发现跳转到首页OK!
5.权限控制展示内容
我们现在又来一个需求:用户没有登录的时候,导航栏上只显示登录按钮,用户登录之后,导航栏可以显示登录的用户信息及注销按钮
要想实现如上功能,首先要导入一个依赖:
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
接着修改我们的前端页面,导入命名空间:
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
修改导航栏,增加认证判断:
<div class="right menu">
<!--如果未登录-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
<!-- 如果已登录 -->
<div sec:authorize="isAuthenticated()">
<a class="item">
<i class="address card icon"></i>
用户名:<span sec:authentication="principal.username"></span>
角色:<span sec:authentication="principal.authorities"></span>
</a>
</div>
<!-- 注销 -->
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
</div>
</div>
现在来测试一下,展示成功!
6.记住我实现
我们只要登录之后,关闭浏览器,再登录,就会让我们重新登录,但是很多网站的情况,就是有一个记住密码的功能,这个该如何实现呢?很简单
开启记住我功能
// 开启记住我功能
http.rememberMe();
我们再次启动项目测试一下,发现登录页多了一个记住我功能,勾选登录之后,我们登录之后关闭 浏览器,然后重新打开浏览器访问,发现用户依旧存在!
7.定制登录页
现在这个登录页面都是spring security 默认的,怎么样可以使用我们自己写的Login界面呢?
在刚才的登录页配置后面指定 loginpage
参数(定制登录也路径):
http.formLogin().loginPage("/toLogin");
然后前端也需要指向我们自己定义的 login请求:
<!--如果未登录-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
login.html里的内容也要改:
<form th:action="@{/toLogin}" method="post">
现在再进行登录,将会跳转到我们自定义的登录页面:
在登录页增加记住我的多选框:
<input type="checkbox" name="remember"> 记住我
后端验证处理!
http.rememberMe().rememberMeParameter("remember");
测试成功!但是注销功能失效了!
解决方案:关闭csrf功能
// 关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.csrf().disable();