前言
Spring Security是一个能够为基于Spring的企业应用系统提供描述性安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IOC(依赖注入,也称控制反转)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
1、Web安全简介
在 Web 开发中,安全一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。
如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:
一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;
另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。
因此,从应用开发的第一天就应该把安全相关的因素考虑进来,并在整个应用的开发过程中。
-
市面上存在比较有名的:Shiro,Spring Security!
-
Spring Security官网介绍:
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
Spring
Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求
Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
- 一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
- 用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
- 用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。
在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。
在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。
2、SpringSecurity概述
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
-
几个重要的类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略 -
@EnableWebSecurity:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
-
“认证”(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。 -
“授权” (Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
3、认证和授权
3.1 引入 Spring Security 模块
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
3.2 编写 Spring Security 配置类
@EnableWebSecurity//开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有用户可以访问,功能页只有对应有权限的人才能访问
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//设置没有权限访问会跳转到登录页
http.formLogin();
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//防止反编译,对明文密码进行编译:BCryptPasswordEncoder()
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("sgx").password(new BCryptPasswordEncoder().encode("123")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("456")).roles("vip1","vip2","vip3")
.and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("789")).roles("vip1","vip3");
}
}
正常业务中:从数据库中读取用户名和密码
注意点:
- 在授权中设置每个角色可以访问的页面,同时设置跳转登录页
- 在认证中设置用户角色;
- Spring security 5.0中新增了多种加密方式,也改变了密码的格式。要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密,spring security 官方推荐的是使用bcrypt加密方式。
3.3 测试
每个用户只能访问对应权限的页面
4、权限控制和注销
4.1注销登录
4.1.1 开启自动配置的注销功能,
注销后默认跳转到登录页,可以设置注销成功后跳转到首页
@Override
protected void configure(HttpSecurity http) throws Exception {
.....
//注销登录,跳回首页(默认会返回到登录页)
http.logout().logoutSuccessUrl("/");
}
4.1.2 在前端页面添加一个注销按钮
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
4.1.3 测试
4.2 权限控制
- 要求1:未登录,只显示登录按钮;登录后,显示用户名,用户权限和注销按钮;
- 要求2:登录成功后,用户只能看到自己权限内的模块;
4.2.1 导入thymeleaf和springSecurity整合包
<!--springSecurity和thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
4.2.2 降低springboot版本,此功能最高支持到2.0.9
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
4.2.3 前端页面导入命名空间,修改前端页面实现要求1
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<!--如果未登录,显示登录-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--如果已登录,显示注销-->
<div sec:authorize="isAuthenticated()">
<a class="item" >
用户名:<span sec:authentication="name"></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>
4.2.4 修改前端页面实现要求B
<div class="column" sec:authorize="hasRole('vip1')">
<div class="column" sec:authorize="hasRole('vip2')">
<div class="column" sec:authorize="hasRole('vip3')">
- 注意:如果点击注销后提示404,是因为它默认防止csrf跨站请求伪造,因为会产生安全问题,我们可以将请求改为post表单提交,或者在spring security中关闭csrf功能;
//关闭csrf防止网站攻击
http.csrf().disable();
5、首页定制和记住我功能
5.1 首页定制:指定登录页
//设置没有权限访问会跳转到登录页
http.formLogin().loginPage("/toLogin");
注意1:当前端表单提交的路径为login,后端需要指定真正权限认定的路由
<form th:action="@{/login}" method="post">
http.formLogin().loginPage("/toLogin").loginProcessingUrl("login");
注意2:当前端表单提交的参数名和后端默认接收的参数名不一致,可以指定接收
<input type="text" placeholder="Username" name="user">
<input type="password" name="pwd">
http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");
5.2 记住我
//开启记住我功能,指定接收的参数名
http.rememberMe().rememberMeParameter("remember");
- 前端页面:
<input type="checkbox" name="remember" /> 记住我
总结:完整的SpringSecurity配置代码
@EnableWebSecurity//开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有用户可以访问,功能页只有对应有权限的人才能访问
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//设置没有权限访问会跳转到登录页
http.formLogin().loginPage("/toLogin")
.usernameParameter("user")
.passwordParameter("pwd")
.loginProcessingUrl("/login");
//关闭csrf防止网站攻击
http.csrf().disable();
//注销登录,跳回首页(默认会返回到登录页)
http.logout().logoutSuccessUrl("/");
//开启记住我功能
http.rememberMe().rememberMeParameter("remember");
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//防止反编译,对明文密码进行编译:BCryptPasswordEncoder()
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("sgx")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("vip2","vip3")
.and()
.withUser("root")
.password(new BCryptPasswordEncoder().encode("456"))
.roles("vip1","vip2","vip3")
.and()
.withUser("admin")
.password(new BCryptPasswordEncoder().encode("789"))
.roles("vip1");
}
}