Spring Security 的介绍和简单使用
Spring Security 的介绍简介平时我们写 Web 项目,都需要用户登录时验证,以及权限管理之类的操作,以前使用过滤器,拦截器等进行管理,原生代码较多。
所以出现了安全框架以供我们使用,安全框架在 Web 应用的主要功能是:
认证授权使用的较多的安全框架有两个:shiro、Spring Security
Spring Security 是 Spring 家族中的一个安全管理框架,相比与另外一个安全框架 Shiro,它提供了更丰富的功能,社区资源也比 Shiro 丰富。
两个框架的主要功能相差不大,核心功能依旧是:认证、授权。
Spring Security 的几个重要类:
WebSecurityConfigurerAdapter:自定义 Sercurity 策略 AuthenticationManagerBuilfer:自定义认证策略 @EnableWebSecurity:开启 WebSecurity 模式 SpringSecurity 的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。
快速使用使用 Spring Boot 集成 Spring Security 结合 Web 应用。
Web 应用准备:
三个等级,不同等级访问不同的区域。
在 maven 项目中导入 Spring Security 的依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>复制代码查看 maven 项目的各个依赖版本:Spring Boot:2.2.1,Spring Security:5.2.1
开始使用 Spring Security 实现授权和认证效果:
// 自定义 Spring Security 的策略// 需要继承 WebSecurityConfigurerAdapter,作用是 --> 启用 Web 安全 @EnableWebSecurity // 该注解包含了 @Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {// 采用的是链式编程
}复制代码授权方法中 http.formLogin(); 源码的注释。
用户登录界面:Spring Security 默认的登录界面,如有需要可以手动修改路径,http.loginPage("url")
登录成功,但是服务器报错:
此时需要对认证部分的代码每个用户的密码进行加密处理。
// 认证 @Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// auth.passwordEncoder() 指定加密方式。Security 5.0+ 提供了很多加密方式 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("zhangSan").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2").and().withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vip1","vip2","vip3").and().withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");}复制代码登陆过后:就会按照上诉的定义的规则,Spring Security 帮我们进行授权和认证操作。
zhangSan:vip1、vip2root:vip1、vip2、vip3user:vip1 每个用户只能根据自己的角色请求授权的资源(请求)。
注销加入用户注销效果:
既然是注销,肯定是授权的一些操作了。
// 开启注销功能 http.logout().logoutSuccessUrl("/"); // 规定注销成功后,重定向到 /复制代码
上图标识了一些注意事项和例子,在前端只要发起/logout 请求即可将用户注销,在代码中还能清除 Cookie 或 Session 等后续操作。
<a href="/logout">注销</a>
权限控制通过上面的代码,一个简单的 Web 应用授权和认证就完成了,只是没有跟网页进行联动,可能不同的角色或用户,在同一个页面看到的菜单栏显示不一样,根据角色的权限不同看到的内容也不同。
所以接下来进行对角色进行权限控制。
如果需要网页根据登录用户的角色进行动态显示内容,则需要 Security 和 thymeleaf 的整合包。
整合包与 Spring Boot 的版本有关:
Spring Boot 2.1.0+ 版本,
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency> 复制代码
html 页面引入
Spring Boot 2.0.9- 版本,
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.4.RELEASE</version> </dependency> 复制代码
html 页面引入
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" 复制代码
此处 Spring Boot 的版本是 2.2.1,导入 thymeleaf-extras-springsecurity5 依赖,在前端页面进行标签设置。
</div>复制代码看到具体的整合包部分标签:
sec:authorize="isAuthenticated()"isAuthenticated 判断是否经过了身份验证,返回 boolean 值。sec:authentication="name"获取当前用户的用户名。sec:authentication="principal.authorities"获取当前用户的所有角色。sec:authorize="hasRole('role')"只有用户角色包括 role 才会显示。效果截图:
登录 zhangSang 用户:
登录 root 用户:
登录 user 用户:
到此,根据用户的角色动态显示内容功能简单实现。
自定义登录界面和开启 RememberMe 功能开启 RememberMe 功能。http.rememberMe();
点击登录就有:
这样就有了 remember me 功能,默认是 14 天时间。
自定义登录界面 login.html
<form th:action="@{/toLogin}" method="post"><input type="text" name="username" placeholder="用户名:"><input type="password" name="password" placeholder="密码:"><input type="checkbox" name="remember"> 记住我<input type="submit" value="登录"></form>
http.formLogin().loginPage("/toLogin");复制代码
登录成功。
这个项目中并没有实现对 toLogin 的登录验证实现,从头到尾都是 Spring Security 框架帮我们进行登录验证的处理,用户就是在内存中写的用户。所以我们修改了两处地方:
准备一个登录界面(login.html)和对于的 Controller (/toLogin),如果是 /login,则会被 Security 拦截直接走默认的 login 页面。修改登录页面的 url:http.formLogin().loginPage("/toLogin");修改登录界面的表单提交的 action:/toLogin 按照这个步骤结合我们以前写的用户验证过程,大概原理应该是 Security 根据 loginPage 指定的登录界面进行拦截处理,并且帮我们提取表单参数从而进行一系列操作。
关键是 loginPage 的 url 与 表单提交的 action 保持一致,这样 Security 才能够拦截成功,经过多次尝试,只要 Controller 中有映射,loginPage 就可以指定该映射,效果一样。
那么帮我们进行表单验证的到底是谁呢?如果没有其他设置的话,默认帮我们处理的还是之前的那个页面表单提交的 url。
这里就引出一个问题:自定义的表单提交的参数是不是需要固定?
默认情况下 Security 的表单验证的部分固定参数可以在源码中看到:
用户名:username 密码:password 记住我选项:remember 如果表单的 name 参数不一致,需要在 Security 配置中进行指定:
// 没有权限会跳到登录页面:源码默认是 /loginhttp.formLogin().loginPage("/to").loginProcessingUrl("/login").usernameParameter("user").passwordParameter("pwd");
// 开启 rememberMe 功能 http.rememberMe().rememberMeParameter("rem");复制代码那么:loginProcessingUrl() 这一配置作用是什么?顾名思义 login 请求发送到指定 URL。
如果 loginPage 与表单提交 action 指定的 URL 不一致,则 loginProcessingUrl()的 URL 与 action URL
保持一致,这样就可以将表单进行拦截验证。
小结学习了 Spring Security 的基本使用,以及和 Thymeleaf 进行整合的使用。
实现 WebSecurityConfigurerAdapter 类,自定义 Security 的一些安全策略重写两个方法,configure() ,一个授权,一个认证整合 Thymeleaf 进行前端的设计,根据配置进行权限控制。以及一些整合时的注意事项和细节其主要通过 AOP 进行授权和认证功能的切入。
而后学习的 Shiro 也跟 Security 有相似之处。
评论