前言:

本文内容:SpringSecurity环境搭建、用户认证和授权、注销及权限控制

推荐免费SpringBoot基础教程视频:【狂神说Java】SpringBoot最新教程通俗易懂_哔哩哔哩_bilibili

SpringSecurity环境搭建

在web开发中,安全第一位!过滤器,拦截器…

网站:安全应该在什么时候考虑? 设计之初

  • 出现漏洞,造成隐私泄露

  • 架构一旦确定,不方便修改

安全框架:shiro SpringSecurity

  • 功能权限
  • 访问权限
  • 菜单权限
  • ……拦截器,过滤器,需要大量原生代码…

简介

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

记住这几个类:

Spring Security的两个主要目标是认证和授权

认证(Authentication)

授权(Authorization)

这两个概念是通用的,并不局限于Spring Security。

环境搭建

  1. 新建项目springboot-06-security,并引入web启动器

  2. 在pom中引入securitythymleaf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--        thymeleaf-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- security-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
  3. 引入静态资源

    百度云下载 提取码:bm3a

  4. controller包下新建RooterController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    package com.jokerdig.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;

    /**
    * @author Joker大雄
    * @data 2022/7/23 - 11:47
    **/
    @Controller
    public class RooterController {

    // 首页
    @RequestMapping({"/","/index","index.html"})
    public String index(){

    return "index";
    }
    // 去登录
    @RequestMapping("toLogin")
    public String toLogin(){

    return "views/login";
    }
    // level1
    @RequestMapping("level1/{id}")
    public String toLevel1(@PathVariable("id") int id){

    return "views/level1/"+id;
    }
    // level2
    @RequestMapping("level2/{id}")
    public String toLevel2(@PathVariable("id") int id){

    return "views/level2/"+id;
    }
    // level3
    @RequestMapping("level3/{id}")
    public String toLevel3(@PathVariable("id") int id){

    return "views/level3/"+id;
    }
    }
  5. 运行测试

    注意:

    您可以在 http://localhost:8080/ 访问应用程序,这会将浏览器重定向到默认登录页面。您可以使用随机生成的密码提供 的默认用户名user,该密码将记录到IDEA控制台中。然后,浏览器将转到最初请求的页面。

    当然:如果你使用旧版的写法就没有上面这一步了,可以直接访问首页!

    要注销,您可以访问 http://localhost:8080/logout,然后确认您希望注销。

  6. 准备工作完成

用户认证和授权

认证(Authentication)

授权(Authorization)

  1. config包下新建SecurityConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package com.jokerdig.config;

    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

    /**
    * @author Joker大雄
    * @data 2022/7/23 - 12:14
    **/
    // 启动Security
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 这里仅用旧版做演示
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http
    .formLogin()
    // 账号和密码接收
    .usernameParameter("username")
    .passwordParameter("password")
    // 不同的功能之间用and()连接
    .and()
    // 权限
    .authorizeRequests()
    .antMatchers("/").permitAll()
    .antMatchers("/level1/**").hasRole("vip1")
    .antMatchers("/level2/**").hasRole("vip2")
    .antMatchers("/level3/**").hasRole("vip3");
    }
    // 这个方法在在新版本失效了,这里仅作演示
    // 密码编码 passwordEncoder
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
    .inMemoryAuthentication()
    .passwordEncoder(new BCryptPasswordEncoder())
    .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
    .and()
    .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
    .and()
    .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");;
    }
    }

    这是新版的写法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    /**
    * @author Joker大雄
    * @data 2022/7/23 - 12:14
    **/
    // 启动Security
    @EnableWebSecurity
    public class SecurityConfig{
    /**
    旧版本的两个方法
    configure(HttpSecurity)
    configure(WebSecurity)
    新版本的代替
    @Bean
    SecurityFilterChain
    @Bean
    WebSecurityCustomizer
    **/

    // 链式编程 设置访问权限 通过 SecurityFilterChain 这是新的写法
    // 新的写法不需要 extends WebSecurityConfigurerAdapter
    // 因为新版没有 configure(AuthenticationManagerBuilder auth),所以使用旧版演示
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
    http
    // 新版formLogin的写法
    .formLogin()
    // 账号和密码接收
    .usernameParameter("username")
    .passwordParameter("password")
    // 不同的功能之间用and()连接
    .and()
    // 权限
    .authorizeRequests()
    .antMatchers("/").permitAll()
    .antMatchers("/level1/**").hasRole("vip1")
    .antMatchers("/level2/**").hasRole("vip2")
    .antMatchers("/level3/**").hasRole("vip3");
    return http.build();
    }
    }
  2. 运行测试

    如果使用新版不能跳转,原因是需要配置AuthenticationSuccessHandler

    也可以降级到2.5.3版本便能正常访问

    发现没有权限只能访问到首页,访问其他页面直接跳转到登录页,登录有权限的账号可以访问对应权限的页面

  3. 认证授权完成

注销及权限控制

注销

  1. Security中编写注销功能及配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    package com.jokerdig.config;

    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    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;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

    /**
    * @author Joker大雄
    * @data 2022/7/23 - 12:14
    **/
    // 启动Security
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 这里仅用旧版做演示
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http
    .formLogin()
    // 账号和密码接收
    .usernameParameter("username")
    .passwordParameter("password")
    // 不同的功能之间用and()连接
    .and()
    // 权限
    .authorizeRequests()
    .antMatchers("/").permitAll()
    .antMatchers("/level1/**").hasRole("vip1")
    .antMatchers("/level2/**").hasRole("vip2")
    .antMatchers("/level3/**").hasRole("vip3");
    // 注销功能 注销成功跳转到首页
    http.logout().logoutSuccessUrl("/");
    }
    // 这个方法在在新版本失效了,这里仅作演示
    // 密码编码 passwordEncoder
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
    .inMemoryAuthentication()
    .passwordEncoder(new BCryptPasswordEncoder())
    .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
    .and()
    .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
    .and()
    .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");;
    }
    }
  2. index.html页面添加注销按钮

    1
    2
    3
    4
    <!--                注销-->
    <a class="item" th:href="@{/logout}">
    <i class="sign-out icon"></i> 注销
    </a>
  3. 运行测试

    点击注销,注销后跳转到首页

  4. 注销功能完成

权限控制

我们希望用户看不到自己权限之外的内容

  1. 在pom引入security-thymeleaf

    1
    2
    3
    4
    5
    6
    <!--        security-thymeleaf整合包-->
    <dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    <version>3.0.4.RELEASE</version>
    </dependency>
  2. 修改index.html页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    <!DOCTYPE html>
    <!--导入security整合thymeleaf的命名空间-->
    <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <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="@{/css/style.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="@{/login}">
    <i class="address card icon"></i> 登录
    </a>
    </div>
    <!--已经登录-->
    <div sec:authorize="isAuthenticated()">
    <!-- 显示用户名-->
    <a class="item">
    用户名:<span sec:authentication="name"></span>
    </a>
    </div>
    <div sec:authorize="isAuthenticated()">
    <!--注销-->
    <a class="item" th:href="@{/logout}">
    <i class="sign-out icon"></i> 注销
    </a>
    </div>
    </div>
    </div>
    </div>

    <div class="ui segment" style="text-align: center">
    <h3>Spring Security</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="@{/js/jquery-3.1.1.min.js}"></script>
    <script th:src="@{/js/semantic.min.js}"></script>

    </body>
    </html>
  3. 运行测试

    根据不同权限显示不同内容,包括用户名显示

    image-20220723170009406

  4. 权限控制功能完成