一、简介
https://docs.spring.io/spring-security/site/docs/4.2.10.RELEASE/guides/html5/helloworld-xml.html
SpringSecurity 融合 Spring 技术栈,提供 JavaEE 应 用的整体安全解决方案;
Spring Security 为基于 Java EE 的企业软件应用提供全面的安全服务。
Spring Security 只需要少量配置,就能构建一个强大的安全的应用系统。
目前市面上受欢迎的两个安全框架:Apache Shiro、SpringSecurity;
SpringSecurity 可以无缝整合 Spring 应用,具有强大的自动化 web 安全管控功能。而 Shiro 是一个轻量级强大的安全框架,可以脱离 web 应用来提供安全管控,但是对于 web 的一些定制安全需要手动编写;SpringBoot 底层默认整合 SpringSecurity 作为安全框架,所以我们推荐 web 应用使用 SpringSecurity 来控制安全;
1、文档
Hello Spring Security <https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/helloworld > 基于 Java 配置整合示例
Hello Spring Security Boot https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/boot/helloworld 与 SpringBoot 整合案例
Hello Spring Security XML https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/xml/helloworld 基于 XML 方式整合示例
Hello Spring MVC Security https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/hellomvc SpringMVC 集成示例
Custom Login Form https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/form 自定义登录表单示例
2、使用方式
一种是全部利用配置文件,将用户、权限、资源(url)硬编码在 xml 文件中
二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置
三种是细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器, 并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。
四是修改 springsecurity 的源代码,主要是修改 InvocationSecurityMetadataSourceService 和 UserDetailsService 两个类。
InvocationSecurityMetadataSourceService
将配置文件或数据库中存储的资源(url)提取出来加工成为 url 和权限列表的 Map 供 Security 使用
UserDetailsService
提取用户名和权限组成一个完整的(UserDetails)User 对象,该对象可以提供用户的详细信息供 AuthentationManager 进行认证与授权使用
3、概念
认证
authenfication
: 身份验证
“身份验证” 是指建立主体(principal)的过程,主体就是他们声称的是谁 (“主体” 通常指用户、设备或在应用程序中可以执行动作的其他系统)。也就是 “证明你是谁”。
授权
authorization
: 授权
“授权” 是指确定主体(principal) 是否被允许执行系统中某个动作的过程。也就是 “你能做什么!”
为了达到“授权”决策(安全框架决定你是否有权限做此事),“身份验证”(authentication)过程已经建立了主体的身份(Principal)
二、SpringSecurity-HellWorld
1、测试环境搭建
1.1 创建普通的 maven-war 工程:spring-security-helloworld
注意:
以下所有的测试都采用的 Idea 集成开发工具
pom 文件增加依赖
1 | <dependencies> |
web.xml 配置
1 |
|
spring 配置:spring.xml
1 |
|
导入实验资源
- 需要资源的博客末尾有下载地址
- 导入页面
- 导入 controller
运行测试
2、引入 SpringSecurity 框架
2.1 添加 security-pom 依赖
1 | <dependency> |
2.2 web.xml 中添加 SpringSecurity 的 Filter 进行安全控制
1 | <filter> |
2.3 加入 SpringSecurity 配置类
- @Configuration、@Bean 注解作用
1 | //声明当前类是一个配置类。相当与XML配置文件作用。 |
2.4 启动测试效果
- 所有资源访问受限(包括静态资源)
- 框架自带一个默认的登录界面
- 账号密码错误会有提示
- 查看登录页面的源码,发现有个 hidden-input: name = “
_csrf
“ 这个是 springsecurity 帮我们防止“跨站请求伪造”
攻击;还可以防止表单重复提交。
三、SpringSecurity 实验
1、实验一: 授权首页和静态资源
- 配置类 (AppWebSecurityConfig extends WebSecurityConfigurerAdapter)
- 重写 configure(HttpSecurity http)方法
1 | // 声明当前类是一个配置类。相当于XML配置文件作用。 |
- 测试结果
静态资源和 index.jsp 都可以访问
不存在的资源
- 有权限无资源 400
- 无权限
2、实验二: 默认及自定义登录页
1 |
|
1 |
|
在访问没有权限资源或页面时,将会自动跳转到index.jsp
登录页面
- 在测试的时候需要先暂时
_csrf
这个功能
1 | http.csrf().disable(); |
3、实验三: 自定义表单登录逻辑分析
1 |
|
表单提交地址:${PATH }/index.jsp
表单提交请求方式:post
提交表单:
- 引入 jquery: <script src=”${PATH }/layui/jquery.min.js”></script>
- $(“form”).submit();
如果没有关闭 CSRF, 提交请求被拒绝, 需要暂时禁用 csrf:http.csrf().disable();
- 表单提交请求失败,提取错误消息:**${SPRING_SECURITY_LAST_EXCEPTION.message}**
1 | <div class="layadmin-user-login-box layadmin-user-login-header"> |
4、实验四: 自定义认证用户信息
1 |
|
输入用户名和密码之后一致,就可以登录页面后台,前提是与自己基于内存认证方式一致的用户名和密码
- CSRF 跨站请求伪造
- SpringSecurity 添加了 crsf 功能 【DefaultCsrfToken】,所有的表单提交为了防止跨站请求的伪造,我们需要加上_csrf 项;或者,暂时禁用
http.csrf().disable();
1 | // 可以在页面任意位置加,没有唯一性 |
5、实验五:用户注销完成
1 |
|
1 | <li class="layui-nav-item"> |
/logout: 退出系统
如果csrf 开启,必须post、方式的/logout 请求,表单中需要增加 csrf token
logoutUrl();退出系统需要发送的请求
logoutSuccessUrl();退出系统成功以后要跳转的页面地址
addLogoutHandler():自定义注销处理器
deleteCookies():指定需要删除的 cookie
invalidateHttpSession():session 失效(DEBUG)
6、实验六:基于角色的访问控制
1 |
|
注意:
1. 将.anyRequest().authenticated()错误的设置在前面,后面的设置就不起作用了。
- 设置所有, “/**“ 都可以访问,其他再进行的设置就不会起作用了
- 设置匿名访问/level3/** 可以不用登录,匿名访问:
.anyRequest().anonymous();
拥有该角色的资源可以访问,否则不可以访问
1 |
|
7、实验七:自定义访问拒绝处理页面
直接增加处理映射界面
1 | http.exceptionHandling().accessDeniedPage("/unauth.html"); |
在控制器类中增加映射处理
1 |
|
增加显示页面,将 main.jsp 复制,命名为 unauth.jsp,增加一句提示信息
1 | <h1>你无权访问该页面...</h1> |
测试显示效果
8、实验八:记住我功能
8.1 记住我功能-免登录原理
1 | http.rememberMe(); |
默认规则
- 页面 checkbox 提交
remember-me
参数
- 默认记住 2 周登录状态:
AbstractRememberMeServices
- 会在cookie中保存名为:remember-me 的 cookie
- 登录后页面,关闭浏览器,直接访问:http://localhost:8080/SpringSecurity/main.html 可以成功访问,不必登录。
- 这种方式,token 值是放置在内存中的,服务器端重启 tomcat,token 会失效。需要将 token 记录在数据库持久化才不会失效。
8.2 记住我-数据版
引入 pom.xml 包
1 | <dependency> |
配置数据源
1 | <!-- 配置数据源 --> |
创建表
1 | create table persistent_logins ( |
设置记住我
1 |
|
四、认证
1、自定义 UserDatailsService 检索用户
创建表结构
1 | CREATE TABLE `t_admin` ( |
配置 configure(AuthenticationManagerBuilder auth)
1 |
|
编写 UserDetailService 实现
1 |
|
运行测试结果,密码不一致,跳转到登录页,并提示错误消息
2、基于数据库(MD5 密码)认证
2.1 配置 configure(AuthenticationManagerBuilder auth)
1 |
|
2.2 引入 MD5 加密工具类:MD5Util.java
1 | package com.oy.security.component; |
2.3 PasswordEncoder 接口实现类:PasswordEncoderImpl
1 |
|
3、基于数据库(BCryptPasswordEncoder)密码加密认证
3.1 PasswordEncoder 接口
3.2 使用 BCryptPasswordEncoder 进行密码加密
1 | /** |
3.3 本地测试
1 | public class BCryptPasswordEncoderTest { |
3.4 服务器运行测试
将生成的密文存储到数据库中(注意:userpswd字段长度
)把 userpaswd 字段长度扩大,重新启动服务器进行测试。