SpringBoot_与安全

一、创建测试工程
 1、引入依赖:这时还没有引入spring-security

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

2、编写前端页面:
SpringBoot_与安全

level1/1.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>罗汉拳</h1>
<p>罗汉拳站当央,打起来不要慌</p>
</body>
</html>

level1/2.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>武当长拳</h1>
<p>长一点在长一点</p>
</body>
</html>

level1/3.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>全真剑法</h1>
<p>全都是真的</p>
</body>
</html>

level2/1.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>太极拳</h1>
<p>
    一个西瓜圆又圆 劈它一刀成两半 你一半来 给你你不要 给他他不收 那就不给 把两人撵走 他们不走你走 走啦,一挥手,伤自尊
    不买西瓜别缠我,缓慢纠缠様 两人缠我赖皮,手慢动作左右挥动 看我厉害,转头缓步拍苍蝇状 拍死了,手抱西瓜状+奥特曼十字手+广播操准备运动的站立
</p>
</body>
</html>

level2/2.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>七伤拳</h1>
<p>练这拳的人全都死了</p>
</body>
</html>

level2/3.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>梯云纵</h1>
<p>踩自己的脚往上跳</p>
</body>
</html>

level3/1.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>葵花宝典</h1>
<p>欲练神功,挥刀自宫</p>
</body>
</html>

level3/2.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>龟派气功</h1>
<p>龟-派-气-功-波</p>
</body>
</html>

level3/3.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<a th:href="@{/}">返回</a>
<h1>独孤九剑</h1>
<p>欲练此剑,必先犯贱</p>
</body>
</html>

login.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
</head>
<body>
<h1 align="center">欢迎登陆武林秘籍管理系统</h1>
<hr>
<div align="center">
    <form action="" method="post">
        用户名:<input name=""/><br>
        密码:<input name=""><br/>
        <input type="submit" value="登陆">
    </form>
</div>
</body>
</html>

welcome.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<h1 align="center">欢迎光临武林秘籍管理系统</h1>
<h2 align="center">游客您好,如果想查看武林秘籍 <a th:href="@{/login}">请登录</a></h2>
<hr>

<h3>普通武功秘籍</h3>
<ul>
    <li><a th:href="@{/level1/1}">罗汉拳</a></li>
    <li><a th:href="@{/level1/2}">武当长拳</a></li>
    <li><a th:href="@{/level1/3}">全真剑法</a></li>
</ul>

<h3>高级武功秘籍</h3>
<ul>
    <li><a th:href="@{/level2/1}">太极拳</a></li>
    <li><a th:href="@{/level2/2}">七伤拳</a></li>
    <li><a th:href="@{/level2/3}">梯云纵</a></li>
</ul>

<h3>绝世武功秘籍</h3>
<ul>
    <li><a th:href="@{/level3/1}">葵花宝典</a></li>
    <li><a th:href="@{/level3/2}">龟派气功</a></li>
    <li><a th:href="@{/level3/3}">独孤九剑</a></li>
</ul>

</body>
</html>

3、编写访问页面的controller:此时访问/时会路由到welcome.html

@Controller
public class KungfuController {
    private final String PREFIX = "pages/";

    /**
     * 欢迎页
     *
     * @return
     */
    @GetMapping("/")
    public String index() {
        return "welcome";
    }

    /**
     * 登陆页
     *
     * @return
     */
    @GetMapping("/userlogin")
    public String loginPage() {
        return PREFIX + "login";
    }


    /**
     * level1页面映射
     *
     * @param path
     * @return
     */
    @GetMapping("/level1/{path}")
    public String level1(@PathVariable("path") String path) {
        return PREFIX + "level1/" + path;
    }

    /**
     * level2页面映射
     *
     * @param path
     * @return
     */
    @GetMapping("/level2/{path}")
    public String level2(@PathVariable("path") String path) {
        return PREFIX + "level2/" + path;
    }

    /**
     * level3页面映射
     *
     * @param path
     * @return
     */
    @GetMapping("/level3/{path}")
    public String level3(@PathVariable("path") String path) {
        return PREFIX + "level3/" + path;
    }
}


4、启动测试:http://localhost:8080/

SpringBoot_与安全
  若SpringBoot版本太低,在访问时可能会因为引入的thymeleaf版本太低(不支持html的一些新标签)而报错,此时可在pom中更改SpringBoot默认引用的thymeleaf的版本:在properties节点中更改

<properties>
	<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
	<thymeleaf-layout-dialect.version>2.3.0</thymeleaf-layout-dialect.version>
</properties>

二、认证和授权
  安全框架的两个主要职能是“认证”和“授权”(或者访问控制),这两个主要职能也是Spring Security 的两个目标。
  认证(Authentication):辨明主体是谁,是建立一个安全主体的过程(主体一般是指用户、设备或一些可以在你的应用程序中执行动作的其他系统)
  授权(Authorization):主体在系统中能干什么,确定一个主体是否允许在你的应用程序执行一个动作(比如查看某个页面资源)的过程。授权之前主体的身份已经有认证,即授权是在认证之后决定主体拥有哪些权限

三、使用Spring Security
 1、引入spring security

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、参照官网编写一个spring security的配置类:示例地址
  示例:配置类继承于WebSecurityConfigurerAdapter,并须添加@EnableWebSecurity注解

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.antMatchers("/css/**", "/index").permitAll()		
				.antMatchers("/user/**").hasRole("USER")			
				.and()
			.formLogin()
				.loginPage("/login").failureUrl("/login-error");	
	}

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth
			.inMemoryAuthentication()
				.withUser("user").password("password").roles("USER");
	}
}

自定义SecurityConfig :定制授权规则

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //定制授权规则
        http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
                .antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
                .antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
                .antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
    }
}

此时再访问未授权的资源时会被拒绝
SpringBoot_与安全
开启登录功能:调用HttpSecurity的formLogin()

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
                .antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
                .antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
                .antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限

        //开启自动配置的登录功能
        http.formLogin();
    }
}

在访问未经授权的资源时会发送/login请求跳转到Security提供的登录页面,如果填写的登录信息有误会发送(login?error)请求跳转到Security提供的登录错误页面,我们也可以设置登录的用户名密码等字段名的信息
   登录页:

SpringBoot_与安全
   登录失败:

SpringBoot_与安全
 3、认证:以上为Security的授权,接下来是Security的认证:

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
                .antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
                .antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
                .antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
        //开启自动配置的登录功能
        http.formLogin();
    }

    /**
     * 开启认证功能
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()//在内存的数据中认证
                .withUser("zhangsan").password("123456").roles("VIP1", "VIP2")//配置用户名、密码和角色信息
                .and()
                .withUser("lisi").password("654321").roles("VIP3");
    }
}

在高版本的Spring Security中使用以上的认证方式可能会报错:

There is no PasswordEncoder mapped for the id "null"

这是因为高版本中需要指定密码的加密方式,代码如下:

/**
 * 开启认证功能
 *
 * @param auth
 * @throws Exception
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	auth.inMemoryAuthentication()//在内存的数据中认证
			.passwordEncoder(new BCryptPasswordEncoder())//指定密码的编码方式
			.withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP2")//配置用户名、密码和角色信息
			.and()
			.withUser("lisi").password(new BCryptPasswordEncoder().encode("654321")).roles("VIP3");//使用Bcrypt对密码进行加密
}

4、注销:调用HttpSecurity.logout()开启注销功能,前端访问/logout请求即可

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
                .antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
                .antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
                .antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限

        //开启自动配置的登录功能
        http.formLogin();

        //开启自动配置的注销功能
        http.logout();
    }
}

前端请求:请求地址/logout,请求方式必须是post

<form th:action="@{/logout}" method="post">
    <input type="submit" value="注销"/>
</form>

开启自动配置的注销功能后只需要访问/logout请求即可清空session(Spring Security已为我们提供了/logout请求的处理,无需我们编写处理逻辑,应该也可以自定义处理逻辑);注销成功后默认会重定向到登录页(访问/login?logout),我们可以定制重定向的路径:设置logoutSuccessUrl的值即可

http.logout().logoutSuccessUrl("/");

四、页面根据授权展示内容
 1、引入Spring Security整合thymeleaf的依赖:注意版本要和Spring Security对应

<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
	<version>3.0.4.RELEASE</version><!-- 该版本可以不指定 -->
</dependency>

由于SpringBoot默认指定了Spring Security和thymeleaf以及他们整合时的版本

<spring-security.version>5.1.3.RELEASE</spring-security.version>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>

但是该版本可能和thymeleaf-extras-springsecurity5不匹配,这时我们可以在pom文件中修改版本:若匹配则无需修改

<properties>
	<thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity.version>
</properties>

2、在页面上引入Security的名称空间

<html xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

在页面上使用引入的名称空间:
  ①未登录则显示登录按钮,否则不显示:sec:authorize="!isAuthenticated()"

<div sec:authorize="!isAuthenticated()">
	<h2 align="center">
		游客您好,如果想查看武林秘籍 
		<a th:href="@{/userlogin}">请登录</a>
	</h2>
</div>

②登录成功显示注销按钮,并显示当前登录者和授予权限:sec:authentication="name"获取登录者,sec:authentication="principal.authorities"获取授予权限

<div sec:authorize="isAuthenticated()">
	<h2>
		<span sec:authentication="name"></span>
		,您好,您的角色有:
		<span sec:authentication="principal.authorities"></span>
	</h2>
	<form th:action="@{/logout}" method="post">
		<input type="submit" value="注销"/>
	</form>
</div>

③根据登录者拥有的角色显示对应的武功秘籍:sec:authorize=“hasRole(‘VIP3’)”

<div sec:authorize="hasRole('VIP3')">
    <h3>绝世武功秘籍</h3>
    <ul>
        <li><a th:href="@{/level3/1}">葵花宝典</a></li>
        <li><a th:href="@{/level3/2}">龟派气功</a></li>
        <li><a th:href="@{/level3/3}">独孤九剑</a></li>
    </ul>
</div>

五、记住我
 1、开启记住我功能:调用HttpSecurity的rememberMe()

protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
			.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
			.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
			.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限

	//开启自动配置的登录功能
	http.formLogin();

	//开启自动配置的注销功能
	http.logout().logoutSuccessUrl("/");

	//开启记住我功能
	http.rememberMe();
}

SpringBoot_与安全
 原理:开启Spring Security的记住我功能之后,一旦我们勾选了记住我,Spring Security就会给浏览器发送一个默认名为remember-me的cookie,默认的有效期限为14天,这样我们14天内(14天后该cookie会自动删除)使用该浏览器再次访问该网址的时候浏览器就会携带着这个cookie,Spring Security就会根据该cookie查找cookie的有效性以及登录人员的信息等,我们点击注销后,Spring Security会删除浏览器端的cookie
SpringBoot_与安全
 注销:
SpringBoot_与安全

上一篇:emacs配置


下一篇:R绘图(2): 离散/分类变量如何画热图/方块图