赞
踩
学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1PE411i7CV
学习文档: 微信公众号 狂神说 –https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=1&sn=3247dca1433a891523d9e4176c90c499&scene=18&uin=&key=&devicetype=Windows+10+x64&version=63020170&lang=zh_CN&ascene=7&fontgear=2
在web开发中,安全是第一位的。安全它是属于应用的非功能性要求, 过滤器、拦截器。在设计之初,就应该考虑进去。否则,如果是在应用开发的后期考虑安全问题的话,一方面,应用存在着严重的泄露问题,可能会泄露用户的隐私数据;另一方面,应用的基本架构已经确立了,如果修改的话,可能需要对系统架构进行很大的调整。这些都会增加开发周期性。所以,web开发的第一步就应该把安全相关因素考虑进去。
关于安全的框架有:shiro、Spring Security,完成的工作是用户认证(Authentication)和用户授权(Authorization)。
认证、授权(vip1,vip2,vip3),原先权限控制的有:
每种框架的出现都是针对某些问题。spring利用IOC 和AOP的思想,将对象创建的控制权由程序猿转交给第三方的IOC容器,低耦合性;面向切面编程AOP同样也是为了不影响原有代码的情况下,将一些通用的功能模块进行封装来切面开发。创建的目标都是为了:高内聚、低耦合,提高代码复用性,增加开发效率。
MVC、Spring、SpringBoot 这种的框架思想不外如是。
Spring Security:官方链接https://spring.io/projects/spring-security#overview
描述:
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 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 是一个功能强大且高度可定制的身份验证和访问控制框架。 它是保护基于 Spring 的应用程序的事实标准。
Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。 像所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义需求
从官网的介绍中可以知道,spring security是一种关于权限的框架。对功能权限,访问权限,和菜单权限等权限的控制。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。安全性主要是两个方面,用户认证和用户授权。
针对这两种问题,Spring Security 框架都有很好的支持。
demo:
新建一个springBoot项目,springboot-06-security 。
关闭模板引擎。application.properties:
# 关闭模板引擎
spring.thymeleaf.cache=false
导入静态资源:
[忘记这个资源来自哪里的博客了,不好意思。!!! 我提供下自己的百度网盘下载链接:链接:https://pan.baidu.com/s/1lRuPSFwb8oi33UR7NTzUUw?pwd=1234
提取码:1234】
Controller跳转
写控制器进行跳转,横切的思想。
package com.al.Controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @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; } }
测试实验环境。启动程序进行测试:
我们根据这个测试的结果,我们会让 vip1 2 3的访问权限不一样,或者让每个人进去之后,展现的界面不一样。 我们该如何去做?
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
我们的测试环境没有问题,任何人都可以访问。我们使用 Spring Security 去增加认证和授权的功能。
1、引入 Spring Security 模块
导入这个 security依赖:
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、编写 Spring Security 配置类
参考官网:https://spring.io/projects/spring-security
查看我们自己项目中的版本,找到对应的帮助文档:
https://docs.spring.io/spring-security/reference/servlet/index.html
首页 index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>首页</title> <!--semantic-ui--> <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet"> <link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet"> </head> <body> <!--主容器--> <div class="ui container"> <div class="ui segment" id="index-header-nav" th:fragment="nav-menu"> <div class="ui secondary menu"> <a class="item" th:href="@{/index}">首页</a> <!--登录注销--> <div class="right menu"> <!--未登录--> <div sec:authorize="!isAuthenticated()"> <!-- <a class="item" th:href="@{/toLogin}">--> <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="sign-out icon"></i> 注销 </a> </div> <!--已登录 <a th:href="@{/usr/toUserCenter}"> <i class="address card icon"></i> admin </a> --> </div> </div> </div> <div class="ui segment" style="text-align: center"> <h3>Spring Security Study by Crazy God</h3> </div> <div> <br> <div class="ui three column stackable grid"> <!--菜单根据用户的角色动态的实现. sec:authorize="hasRole('vip1')"--> <div class="column"> <div class="column" sec:authorize="hasRole('vip1')"> <!-- <div class="ui raised segment">--> <div class="ui"> <div class="content"> <h5 class="content">Level 1</h5> <hr> <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div> <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div> <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div> </div> </div> </div> </div> <div class="column"> <div class="ui raised segment" sec:authorize="hasRole('vip2')"> <div class="ui"> <div class="content"> <h5 class="content">Level 2</h5> <hr> <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div> <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div> <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div> </div> </div> </div> </div> <div class="column"> <div class="ui raised segment" sec:authorize="hasRole('vip3')"> <div class="ui"> <div class="content"> <h5 class="content">Level 3</h5> <hr> <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div> <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div> <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div> </div> </div> </div> </div> </div> </div> </div> <script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script> <script th:src="@{/qinjiang/js/semantic.min.js}"></script> </body> </html>
新建一个config文件夹,用于进行配置类文件。
创建 SecurityConfig ,需要继承 WebSecurityConfigurerAdapter。
package com.al.config; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; // AOP:拦截器 @EnableWebSecurity 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"); } }
然后进行测试, 查看访问的结果。
发现除了首页都进不去了!因为我们目前没有登录的角色,因为请求需要登录的角色拥有对应的权限才可以!
【注意点: index.html 中的登录提交表单响应的url请求是 /login。就是spring security 默认的。原来如此啊。 后面解决问题的时候才发现这个问题。 th:href=“@{/toLogin}” 是为了自己写定制登录页面的。我说怎么一直不对劲】
<!--未登录-->
<div sec:authorize="!isAuthenticated()">
<!-- <a class="item" th:href="@{/toLogin}">-->
<a class="item" th:href="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
添加 当用户没有权限进入指定页面时,让其跳转到登录页面:
在configure()方法中加入以下配置,开启自动配置的登录功能!
protected void configure(HttpSecurity http) throws Exception {
// 首页所有人可以访问, 功能页只有对应权限的人才访问
// 请求授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
// 开启自动配置的登录功能。没有权限默认会到登录界面,需要开启登录的页面
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin();
}
测试,此时,点击首页的选项,会直接跳转到登录页面,让其进行登录。
但是这时候无法登录成功,不能进行登录。我们还需要进行配置。
我们可以定义认证规则,重写configure(AuthenticationManagerBuilder auth)方法。
SecurityConfig 中的认证代码:
// 定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿.这些数据正常情况下应该从数据库中读
auth.inMemoryAuthentication()
.withUser("alzn").password("123456").roles("vip1","vip2","vip3")
.and()
.withUser("al").password("123456").roles("vip1","vip2")
.and()
.withUser("zn").password("123456").roles("vip3");
}
运行测试,报错。 提示我们的密码没有加密:
控制台输出:java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
我们要将前端传过来的密码进行某种方式加密,否则就无法登录。修改代码:
// 定义认证规则:springboot 2.1.x 还可以直接使用
// 密码编码:PasswordEncoder
// 在Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿.这些数据正常情况下应该从数据库中读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("alzn").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("al").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
.and()
.withUser("zn").password(new BCryptPasswordEncoder().encode("123456")).roles("vip3");
}
运行测试,发现每个角色只能访问自己认证下的规则 的页面信息。
比如,alzn 可以访问所有的页面;zn只能访问 level3 即vip3的页面信息。
验证了用户alzn的合法性,权限是vip1、vip2、vip3;授权vip1、vip2、vip3能够执行的操作。
开启自动配置中的注销功能。
在定制请求的授权规则中,去开启注销功能。
// 定制请求的授权规则 @Override protected void configure(HttpSecurity http) throws Exception { // 首页所有人可以访问, 功能页只有对应权限的人才访问 // 请求授权的规则 http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); // 开启自动配置的登录功能。没有权限默认会到登录界面,需要开启登录的页面 // /login 请求来到登录页 // /login?error 重定向到这里表示登录失败 http.formLogin(); //注销,开启注销功能 // /logout 注销请求 http.logout(); }
我们在前端,增加一个注销的按钮,index.html 导航栏中
<!--注销-->
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aw5k0URR-1651928369131)(…/…/…/…/…/Users/ALZN/AppData/Roaming/Typora/typora-user-images/image-20220503213251430.png)]
测试,我们在登录成功后去点击注销,发现注销完毕会跳转到登录页面!
跳转到登录页面:
但是,我们想要==在注销成功之后跳转到首页==,那么如何去做呢?
对**定制请求的授权规则 configure(HttpSecurity http) **中开启注销配置功能进行修改。如下所示:
.logoutSuccessUrl(“/”); 注销成功来到首页
//注销,开启注销功能
// /logout 注销请求
//http.logout();
http.logout().logoutSuccessUrl("/"); // .logoutSuccessUrl("/"); 注销成功来到首页
测试,在注销完毕之后能够成功跳转到首页 index.html 。
我们现在又来一个需求:用户没有登录的时候,导航栏上只显示登录按钮,用户登录之后,导航栏可以显示登录的用户信息及注销按钮!还有就是,比如 al 这个用户,它只有 vip1,vip2功能,那么登录则只显示这两个功能,而vip3的功能菜单不显示!这个就是真实的网站情况了!该如何做呢?
我们需要结合thymeleaf中的一些功能:
sec:authorize=“isAuthenticated()”:是否认证登录!来显示不同的页面
导入maven依赖:
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<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"
修改导航栏,增加认证判断。
index.html:
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu"> <div class="ui secondary menu"> <a class="item" th:href="@{/index}">首页</a> <!--登录注销--> <div class="right menu"> <!--未登录--> <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"> <i class="address card icon"&
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。