7.1 SpringSecurity安全框架

一.SpringSecurity概述

1.什么是安全

  • 在web开发中,安全第一位!之前使用的过滤器和拦截器都是为了安全的目的。

  • 不是网站的功能性需求

  • 应该在网站设计之初就考虑安全,防止隐私泄露等安全问题

  • shiro、SpringSecurity框架:除了类不同和名字不一样,基本很像都是认证和授权等。

2.Spring Security简介

(1)SpringSecurity是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理。

(2)几个重要的类

  • WebSecurityConfigurerAdapter:自定义Security策略

  • AuthenticationManagerBuilder:自定义认证策略

  • @EnableWebSecurity:开启WebSecurity模式,@Enablexxxx开启某个功能

(3)SpringSecurity的两个主要目标是“认证”和“授权”(访问控制)

  • “认证”(Authentication)

  • “授权”(Authorization)

  • 这个概念是通用的,而不是只在SpringSecurity中存在

(4)springsecurity网址

  • 官网:https://spring.io/projects/spring-security

  • JavaConfig配置:https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/htmlsingle/#jc

3.使用Spring Security

(0)步骤:项目根据用户权限显示不同权限用户所能见的内容

  • 新建springboot项目,添加springmvc,springsecurity,thymeleaf等模块(略)

  • pom.xml导入依赖thymeleaf-extras-springsecurity5

  • 导入静态资源和页面并配置管理thymeleaf缓存

  • 编写路由控制器

  • 以Java Config方式编写配置文件

  • 前端页面使用thymeleaf-extras-springsecurity5

  • 测试

(1)pom.xml导入依赖thymeleaf-extras-springsecurity5

  • thymeleaf-extras-springsecurity5是thymeleaf和springsecurity所整合的包

<!--thymeleaf和springsecurity整合包-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

(2)导入静态资源和页面并配置管理thymeleaf缓存

spring.thymeleaf.cache=false

(3)编写路由控制器

  • 建立controller文件夹并在其下建立RouterController.java

 1 import org.springframework.stereotype.Controller;
 2 import org.springframework.web.bind.annotation.PathVariable;
 3 import org.springframework.web.bind.annotation.RequestMapping;
 4 
 5 @Controller
 6 public class RouterController {
 7 
 8     @RequestMapping({"/", "/index"})
 9     public String index() {
10         return "index";
11     }
12 
13     @RequestMapping("/toLogin")
14     public String toLogin() {
15         return "views/login";
16     }
17 
18     @RequestMapping("/level1/{id}")
19     public String level1(@PathVariable("id") int id) {
20         return "views/level1/" + id;
21     }
22 
23     @RequestMapping("/level2/{id}")
24     public String level2(@PathVariable("id") int id) {
25         return "views/level2/" + id;
26     }
27 
28     @RequestMapping("/level3/{id}")
29     public String level3(@PathVariable("id") int id) {
30         return "views/level3/" + id;
31     }
32 }

(4)以Java Config方式编写配置文件

  • 在controller同一级添加config文件夹并在其下建立SecurityConfig.java文件

  • 重写授权方法:

    • 此处设置了四个授权:首页所有人可访问,level1目录下的页面等级vip1可访问,level2目录下的页面等级vip2可访问,level3目录下的页面等级vip3可访问

    • 如果没有权限访问默认跳转到springsecurity的登陆页面

    • 定义登出页面为首页

    • 关闭跨站脚本攻击

  • 重写认证方法

    • 设置了三个用户以及这三个用户的权限

 1 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 2 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 3 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 4 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 5 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 6 
 7 //AOP
 8 @EnableWebSecurity
 9 public class SecurityConfig extends WebSecurityConfigurerAdapter {
10 
11     // 授权
12     @Override
13     protected void configure(HttpSecurity http) throws Exception {
14 
15 
16         //首页所有人可以访问,功能页只有对应有权限的人才可以访问
17         //请求授权的规则,链式编程
18         http.authorizeRequests()
19                 .antMatchers("/").permitAll()
20                 .antMatchers("/level1/**").hasRole("vip1")
21                 .antMatchers("/level2/**").hasRole("vip2")
22                 .antMatchers("/level3/**").hasRole("vip3");
23 
24         // 没有权限默认会到登录页面,需要开启登录的页面login
25         http.formLogin();
26 
27         // 注销,开启注销功能,注销后会跳转到/logout
28         // 可以清空Cookie和Session
29         //http.logout().deleteCookies("remove").invalidateHttpSession(true);
30         http.logout().logoutSuccessUrl("/");
31 
32         // 防止跨站脚本攻击关闭
33         http.csrf().disable();
34     }
35 
36     // 认证,在Spring Security 5.0+中需要对密码加密passwordEncoder
37     @Override
38     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
39 
40         // 这些数据正常从数据库中读,此处使用inMemoryAuthentication从内存中读
41         auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
42                 .withUser("wzh").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
43                 .and()
44                 .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
45                 .and()
46                 .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
47     }
48 }

(5)前端页面使用thymeleaf-extras-springsecurity5

  • 定义命名空间: xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" 

  • 判断是否登录: sec:authorize="!isAuthenticated()" 

  • 获得登录用户名: sec:authentication="name" 

  • 获得登录用户权限: sec:authentication="principal.authorities" 

  • 判断是否有xxx权限: sec:authorize="hasRole('vip1')" 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<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}">
                        <i class="address card icon"></i> 登录
                    </a>
                </div>

                <!--已登录显示用户名-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        用户名:<span sec:authentication="name"></span>&nbsp;&nbsp;
                        角色:<span sec:authentication="principal.authorities"></span>&nbsp;&nbsp;
                    </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 秦疆</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">
            <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" sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <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" sec:authorize="hasRole('vip3')">
                <div class="ui raised segment">
                    <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>

(6)测试

  • guest用户

  • wzh用户

  • root用户

4.登录定制和记住我功能

(1)步骤:

  • 修改SecurityConfig.java文件

  • 修改login.html

  • 测试

(2)修改SecurityConfig.java文件

  • 登录页面为控制器的/toLogin方法跳转的页面,Security处理的登录请求为/login,用户名从user参数中拿到,密码从pwd参数中拿到,记住我从remember参数中拿到

1 http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login").usernameParameter("user").passwordParameter("pwd");
2 http.rememberMe().rememberMeParameter("remember");
  • 完整代码

 1 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 2 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 3 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 4 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 5 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 6 
 7 //AOP
 8 @EnableWebSecurity
 9 public class SecurityConfig extends WebSecurityConfigurerAdapter {
10 
11     // 授权
12     @Override
13     protected void configure(HttpSecurity http) throws Exception {
14 
15 
16         //首页所有人可以访问,功能页只有对应有权限的人才可以访问
17         //请求授权的规则,链式编程
18         http.authorizeRequests()
19                 .antMatchers("/").permitAll()
20                 .antMatchers("/level1/**").hasRole("vip1")
21                 .antMatchers("/level2/**").hasRole("vip2")
22                 .antMatchers("/level3/**").hasRole("vip3");
23 
24         // 1.没有授权会自动跳转到Spring Security自带的登录页面 http://localhost:8080/login
25         // 2.定制登录页:配置loginPage("/toLogin") 会调用控制器方法跳转到http://localhost:8080/toLogin
26         //   表单点击提交后跳转到 http://localhost:8080/login(因为要认证用户,使用Spring Security),表单的action动作为th:action="@{/login}"
27         //   其对应 http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login")
28         // 3.配置表单提交来的用户名为user,密码为pwd
29         //   <input type="text" placeholder="Username" name="user">
30         //   <input type="password" name="pwd">
31         http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login")
32                 .usernameParameter("user").passwordParameter("pwd");
33 
34         // 注销,开启注销功能,注销后会跳转到/logout
35         // 可以清空Cookie和Session
36         //http.logout().deleteCookies("remove").invalidateHttpSession(true);
37         http.logout().logoutSuccessUrl("/");
38 
39         // 防止跨站脚本攻击关闭
40         http.csrf().disable();
41 
42         // 开启记住我功能,默认Cookie为14天过期,自定义接收前端参数
43         // <input type="checkbox" name="remember">记住我
44         http.rememberMe().rememberMeParameter("remember");
45     }
46 
47     // 认证,在Spring Security 5.0+中需要对密码加密passwordEncoder
48     @Override
49     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
50 
51         // 这些数据正常从数据库中读,此处使用inMemoryAuthentication从内存中读
52         auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
53                 .withUser("wzh").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
54                 .and()
55                 .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
56                 .and()
57                 .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
58     }
59 }

(3)修改login.html

  • 提交/login

  • 参数user,pwd,remember

 1 <form th:action="@{/login}" method="post">
 2     <div class="field">
 3         <label>Username</label>
 4         <div class="ui left icon input">
 5             <input type="text" placeholder="Username" name="user">
 6             <i class="user icon"></i>
 7         </div>
 8     </div>
 9     <div class="field">
10         <label>Password</label>
11         <div class="ui left icon input">
12             <input type="password" name="pwd">
13             <i class="lock icon"></i>
14         </div>
15     </div>
16     <div class="field">
17         <input type="checkbox" name="remember">记住我
18     </div>
19     <input type="submit" class="ui blue submit button"/>
20 </form>

(4)测试成功

 

posted @ 2020-03-04 18:02  All_just_for_fun  阅读(479)  评论(0编辑  收藏  举报