zoukankan      html  css  js  c++  java
  • 初窥SpringSecurity安全框架

    @

    作为一名开发怎能不知道大名顶顶的安全框架呢?市面上流行的安全框架有:shiro和springSecurity。那么你经常用哪个框架做安全访问控制呢?因为SpringBoot集成了SpringSecurity,所以我们这次来聊聊它

    概念

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

    SpringSecurity官网:官网

    对应依赖

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

    SpringSecurity包含的模块:

    继续,我们可以看到这么一幅图,可以知道它是基于servlet过滤器实现的:

    http配置相关:

    用户密码验证:

    创建项目

    1.创建新的项目,选择thymeleaf和SpringWeb依赖
    2.新增静态资源(代码省略,主要是各角色页面,需要此部分代码可私我)
    3.添加路由控制页面跳转

    package com.springstudy.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";
        }
    
        // 通过restful 实现复用
        @RequestMapping("/level1/{id}")
        public String level1(@PathVariable("id") int id){
            return "views/level1/"+id;
        }
    
        @RequestMapping("/level2/{id}")
        public String level12(@PathVariable("id") int id){
            return "views/level2/"+id;
        }
    
        @RequestMapping("/level3/{id}")
        public String level3(@PathVariable("id") int id){
            return "views/level3/"+id;
        }
    }
    

    4.启动项目查看页面效果(默认登陆用户名是user,登陆密码在控制台)

    后面我们要实现对应的权限控制效果!

    自定义登陆用户和密码

    老是这样登不行啊,我么来自定义配置一下登陆用户名和密码

    # 清除thymeleaf缓存 这使得我们改动html代码不用重启项目 build一下即可生效
    spring.thymeleaf.cache=false
    # 用户名
    spring.security.user.name=admin
    # 密码
    spring.security.user.password=123456
    

    新增SecurityConfig配置类

    package com.springstudy.config;
    
    import org.springframework.context.annotation.Configuration;
    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;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        // 可以再这里导入userService相关配置
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 不同用户访问内容不同! 这里,过滤器,登录注销规则,安全配置,OAuth2配置
            // 我们平时只需要配置一些基本的规则即可!
    
            // 首页是允许所有人访问的!
            // 定制授权规则: 那些请求,哪些人可以访问!
            http.authorizeRequests()
                    .antMatchers("/").permitAll()
                    .antMatchers("/level1/**").hasRole("guest")
                    .antMatchers("/level2/**").hasRole("vip")
                    .antMatchers("/level3/**").hasRole("svip");
    
            // 自定义登录页 配置后默认登陆页将失效
            // login跳转到登录页  /login?error 登录失败
            http.formLogin()
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .loginPage("/toLogin")
                    .loginProcessingUrl("/login"); // 登陆表单提交请求!
    
            // 如果注销404,因为 Security 默认是防止 csrf 跨站伪请求!
            // http.csrf().disable(); // 可能会让我们系统不安全
    
            // 注销 开启默认的注销功能!
            http.logout().logoutSuccessUrl("/"); // 注销成功后跳转至首页!
    
            // 自定义的登录页需要配置 rememberMe 的参数名,就可以绑定到我们前端的!
            // 记住我功能
            http.rememberMe().rememberMeParameter("remember");
        }
    
        // 定义用户的认证规则 (角色,密码....)
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // 定义user1 uer2 user3 三个用户的角色权限
            // 这里我们一般在用户角色表里存储用户的角色信息
            // 密码没有加密会报500错误,这里我们只定义了用户的密码加密,但是没有定义用户的认证规则的加密方式
            auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                    // 一个人可以拥有多个角色!
                    .withUser("user1")
                    .password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("guest")
                    .and()
                    .withUser("user2")
                    .password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("guest","vip")
                    .and()
                    .withUser("user3")
                    .password(new BCryptPasswordEncoder().encode("123456"))
                    .roles("guest","vip","svip");
        }
    }
    

    修改前台配置

    <!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="@{/test/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">
                     <!--核心类:Authentication-->
                    <!-- 如果未登录就显示登陆按钮 -->
                    <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"></i>-->
                            用户名:<span sec:authentication="principal.username"></span> &nbsp;
                            角色:<span sec:authentication="principal.authorities"></span>
                        </a>
                    </div>
    
                    <div sec:authorize="isAuthenticated()">
                        <!-- 将注销请求也改成post提交即可! -->
                        <form th:action="@{/logout}" method="post">
    <!--                        <button type="submit" class="def-log-out">注销</button>-->
                            <a class="item" th:href="@{/logout}" style="text-decoration:underline;">
                                注销
                            </a>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    
        <div class="ui segment" style="text-align: center">
            <h3>Spring Security Study</h3>
        </div>
    
        <div>
            <br>
            <div class="ui three column stackable grid">
    <!--            <div sec:authorize="hasRole('vip1')">-->
                    <div class="column" sec:authorize="hasRole('guest')">
                        <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 sec:authorize="hasRole('vip2')">-->
                    <div class="column" sec:authorize="hasRole('vip')">
                        <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 sec:authorize="hasRole('vip3')">-->
                    <div class="column" sec:authorize="hasRole('svip')">
                        <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="@{/test/js/jquery-3.1.1.min.js}"></script>
    <script th:src="@{/test/js/semantic.min.js}"></script>
    </body>
    </html>
    

    重启项目验证

    可以看到3个用户,且不同的模块,实现了不同的权限控制。
    注:在我们配置注销时,可以看到springSecurity已经帮我们配置好了

    当然我们实际的处理方式是不同的角色配置不同的菜单,不会这么控制!这里只是举例说明Security权限控制以及thymeleaf权限控制语法。Sercurity还是登陆鉴权用的多!

    登陆页配置:记住我

    功能:用户没有登录的时候,就只显示导航栏!如果登录了,只显示自己权限能够看到的东西 根据不同的权限,前端展示不同的功能!

    springSecrity 和 thymealef 结合:

    添加依赖:

    <!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras- springsecurity5 -->
    <dependency>
        <groupId>org.thymeleaf.extras</groupId> 
        <artifactId>thymeleaf-extras-springsecurity5</artifactId> 
        <version>3.0.4.RELEASE</version>
    </dependency>
    

    前端代码:

    <input type="checkbox" name="remember"> 记住我
    

    后端代码:

     // 自定义的登录页需要配置 rememberMe 的参数名,就可以绑定到我们前端的! 
     // 记住我功能 
     http.rememberMe().rememberMeParameter("remember");
    
     // 定制登陆页
     http.formLogin()
        .usernameParameter("username") // 前端用户名提交参数 
        .passwordParameter("password") // 前端密码提交参数 
        .loginPage("/toLogin") // 登录页面 
        .loginProcessingUrl("/login"); // 登陆表单提交请求!
    

    启动测试看用户信息是否存在cookie,默认过期时间15天:

    可以看到对应有个remember-me的cookie,至于这个key为何是remember-me可以去看源码,一切都配置都能去源码中找到答案!删除cookie后刷新页面看看是否自动退出了!

    退出的问题

    点击退出发现报错了

    这是为什么呢?
    原来SpringSecurity禁止了get方式的退出,以防止 csrf 跨站伪请求!

    // 如果注销404,因为 Security 默认是防止 csrf 跨站伪请求!
    // http.csrf().disable(); // 可能会让我们系统不安全
    

    那我们把退出操作改成表单提交的post方式请求即可;

    修改index.html注销代码:

    <form th:action="@{/logout}" method="post">
        <button type="submit" class="def-log-out">注销</button>
        <!-- <a class="item" th:href="@{/logout}" style="text-decoration:underline;">
            注销
        </a> -->
    </form>
    

    build 后再次点击注销可以验证下!

    余路那么长,还是得带着虔诚上路...
  • 相关阅读:
    PHP基础学习笔记(一)
    安装wampserver之后,浏览器中输入localhost页面显示IIS7解决办法
    HTML5常识总结(一)
    AngularJs中的服务
    AngularJs中的directives(指令part1)
    Happy Number——LeetCode
    Binary Tree Zigzag Level Order Traversal——LeetCode
    Construct Binary Tree from Preorder and Inorder Traversal——LeetCode
    Construct Binary Tree from Inorder and Postorder Traversal——LeetCode
    Convert Sorted Array to Binary Search Tree——LeetCode
  • 原文地址:https://www.cnblogs.com/itiaotiao/p/12736426.html
Copyright © 2011-2022 走看看