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、编写前端页面:
在这里插入图片描述
  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版本太低,在访问时可能会因为引入的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权限
    }
}

  此时再访问未授权的资源时会被拒绝:
在这里插入图片描述
  开启登录功能:调用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提供的登录错误页面,我们也可以设置登录的用户名密码等字段名的信息
   登录页:
在这里插入图片描述
   登录失败:
在这里插入图片描述
 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();
}

 开启后在登录页面会出现记住我的复选框,勾选后即可记住我:
在这里插入图片描述
 原理:开启Spring Security的记住我功能之后,一旦我们勾选了记住我,Spring Security就会给浏览器发送一个默认名为remember-me的cookie,默认的有效期限为14天,这样我们14天内(14天后该cookie会自动删除)使用该浏览器再次访问该网址的时候浏览器就会携带着这个cookie,Spring Security就会根据该cookie查找cookie的有效性以及登录人员的信息等,我们点击注销后,Spring Security会删除浏览器端的cookie
在这里插入图片描述
 注销:
在这里插入图片描述
 2、定制登录页面
  ①自定义登录请求地址:此设置仅仅是指定跳转到登录页的请求,以便于我们自定义登录页面

http.formLogin().loginPage("/userlogin");

  注意:一旦我们自定义了登录页,则登录请求的地址也会变成我们设置的loginPage值,但须是post方式的请求,参考③。除非我们在代码中明确指定登录请求的地址loginProcessingUrl:此时我们在form表单中可以使用/login的post请求进行登录

http.formLogin().loginPage("/userlogin").loginProcessingUrl("/login");

  如果我们没有明确指定loginProcessingUrl,则Spring Security会将我们设置的loginPage的值的post请求转发到Spring Security的/login的post请求,而我们直接请求/login时Spring Security是不认的,也就是说其实真正的登录功能本身还是Spring Security提供的/login,且必须以post的方式请求,我们可以自定义登录页面,但不能自定义登录功能本身,否则就会脱离Spring Security
  ②修改前端进入登录页请求地址:

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

  ③设置前端登录请求地址:用户域的字段名默认为username,密码域的字段名默认为password,这两个域的字段名可以自定义,但必须和自定义的保持一致

<form th:action="@{/userlogin}" method="post">
	用户名:<input name="username"/><br>
	密码:<input name="password"><br/>
	<input type="submit" value="登陆">
</form>

  自定义字段名:

http.formLogin().usernameParameter("user").passwordParameter("pwd").loginPage("/userlogin");

  页面传参要保持一致:

<form th:action="@{/userlogin}" method="post">
	用户名:<input name="user"/><br>
	密码:<input name="pwd"><br/>
	<input type="submit" value="登陆">
</form>

  综上所述:如果我们设置了loginProcessUrl,则登录方式为:

http.formLogin().usernameParameter("user").passwordParameter("pwd").loginPage("/userlogin").loginProcessingUrl("/login")

  登录请求地址为我们设置的/login,请求方式为post:

<form th:action="@{/login}" method="post">
	用户名:<input name="user"/><br>
	密码:<input name="pwd"><br/>
	<input type="submit" value="登陆">
</form>

  若未设置loginProcessUrl,则登录方式为:

http.formLogin().usernameParameter("user").passwordParameter("pwd").loginPage("/userlogin");

  登录请求地址为我们loginPage的值/userlogin,请求方式为post:

<form th:action="@{/userlogin}" method="post">
	用户名:<input name="user"/><br>
	密码:<input name="pwd"><br/>
	<input type="submit" value="登陆">
</form>

  ④给自定义登录页加rememberMe功能:添加一个checkbox类型的input域,name值为remember-me

<form th:action="@{/userlogin}" method="post">
	用户名:<input name="user"/><br>
	密码:<input name="pwd"><br/>
	<input type="checkbox" name="remember-me"/>记住我<br/>
	<input type="submit" value="登陆">
</form>

在这里插入图片描述
  自定义记住我的参数名:cookie的名字、时效等都可以自定义

http.rememberMe().rememberMeParameter("remember");

  input域的checkbox的name值要和自定义的保持一致:

<form th:action="@{/userlogin}" method="post">
	用户名:<input name="user"/><br>
	密码:<input name="pwd"><br/>
	<input type="checkbox" name="remember"/>记住我<br/>
	<input type="submit" value="登陆">
</form>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值