[spring] Spring MVC - security(上)

[spring] Spring MVC - security(上)

这部分的内容基本上和 [spring] rest api security 是重合的,主要就是添加 验证(authentication)和授权(authorization)这两个功能

即:

  • 用户提供的验证信息是否正确
  • 用户是否有权限访问当前资源

整体流程大致如下:

auth flowchart

项目设置

这里依旧使用 https://start.spring.io/ 去进行配置,需要的 POM 如下:

在这里插入图片描述

这里和 [spring] rest api security 有区别的地方在于添加了一个 thymeleaf 的依赖:

在这里插入图片描述

这个也是 https://start.spring.io/ 自动添加的

基础 view

spring boot 会自动实现一个登录的页面,这里主要是新建一个 DemoController 去进行路径的 mapping,即提供一个登录完成后重定向的页面

代码实现如下:

  • java controller

    @Controller
    public class DemoController {
        @GetMapping("/")
        public String showHome() {
            return "home";
        }
    }
    
    
  • HTML 模板

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Home</title>
      </head>
      <body>
        <h2>Home Page</h2>
        <hr />
        Dummy Home Page
      </body>
    </html>
    

实现效果如下:

在这里插入图片描述

⚠️:这个登录页面是 spring boot 实现的

在没有任何配置的情况下,spring boot 默认提供的用户名是 admin,密码则是自动生成的一串哈希值,会在终端显现:

在这里插入图片描述

用户信息验证成功后,就会重定向到 mapping 好的首页:

在这里插入图片描述

基本安全配置

这里就是在代码里手动写死用户名、密码和权限,这个目前是为了简单实现,后面会添加数据库部分的实现

java 代码如下:

@Configuration
public class DemoSecurityConfig {
    @Bean
    public InMemoryUserDetailsManager userDetailsManager() {
        UserDetails john = User.builder()
                .username("john")
                .password("{noop}test123")
                .roles("EMPLOYEE")
                .build();

        UserDetails mary = User.builder()
                .username("mary")
                .password("{noop}test123")
                .roles("EMPLOYEE", "MANAGER")
                .build();

        UserDetails susan = User.builder()
                .username("susan")
                .password("{noop}test123")
                .roles("EMPLOYEE", "MANAGER", "ADMIN")
                .build();

        return new InMemoryUserDetailsManager(john, mary, susan);
    }
}

配置完并自动重启项目后,内存中的用户信息就具有更高的权重值,spring boot 也不会自动生成哈希值去和 admin 进行适配

自定义登录页面

这里有 3 个步骤要去做:

  1. 重新写 spring 的安全配置,使用自己的 HTML 模板取代 spring boot 内置的 HTML 模板

    具体实现如下:

    @Configuration
    public class DemoSecurityConfig {
        // 省略 inMemoryUserDetails 的实现
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.authorizeHttpRequests(configurer ->
                    configurer
                            .anyRequest().authenticated()
                    ).formLogin(form ->
                        form
                                .loginPage("/showMyLoginPage")
                                .loginProcessingUrl("/authenticateUser")    // no controller request mapping for this
                                .permitAll()
                    );
    
            return httpSecurity.build();
        }
    }
    

    其中:

    • SecurityFilterChain 主要是用来处理 HTTP 请求,对其进行安全处理

    • HttpSecurity 则是具体对 HTTP 请求进行安全处理的配置

    • authorizeHttpRequests 代表所有的 HTTP 请求都必须要进行安全处理,即登录验证

      简单的说,访客是没有权限访问当前应用

    • formLogin 是表单登录验证

      这里主要进行 3 个处理

      1. loginPage 是登录页面的路径

      2. loginProcessingUrl 是提交登录信息的路径

        参考之前在 [spring] Spring MVC & Thymeleaf(上) 中实现的@RequestMapping("/processForm")"

        不过这个路径会被 spring 在内部处理,所以不需要手动实现一个 controller 去完成功能

    1. permitAll() 代表所有人都可以访问,包括访客

      这是一定要加的,不然登录页面本身就会需要用户验证

  2. 在 controller 层进行配置,对登录页面进行重定向

    
    @Controller
    public class LoginController {
        @GetMapping("/showLoginPage")
        public String showLoginPage() {
            return "plain-login";
        }
    }
    
    

    这是另一个 controller,专门负责登录页面的重定向,与 DemoController 不一样

    可以理解成这个 controller 负责的是所有不需要验证信息的访问,包括后面会处理的报错页面

  3. 实现 HTML 模板引擎

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
        <meta charset="UTF-8" />
        <title>Custom Login Page</title>
      </head>
      <body>
        <h3>My Custom Login Page</h3>
        <form method="post" action="#" th:action="@{/authenticateUser}">
          <p>
            <label for="username">Username:</label>
            <input type="text" name="username" id="username" />
          </p>
    
          <p>
            <label for="password">Password:</label>
            <input type="password" name="password" id="password" />
          </p>
    
          <input type="submit" value="Login" />
        </form>
    
        <script src="http://localhost:35729/livereload.js"></script>
      </body>
    </html>
    

    其中 th:action="@{/authenticateUser}" 这个语法是将 authenticateUser 绑定到当前路径下。如当前路径为 http://localhost:8080/sighup,那么这个表单提交的 URL 为 http://localhost:8080/sighup/authenticateUser。这样实现的优点在于不用写死路径

添加错误信息

目前登录页面是没有报错信息的,想要解决这个方法也很简单,可以使用 error 这个状态:

在这里插入图片描述

⚠️:这是 spring boot 实现的自动重定向,想要修改的话也可以在 formLogin 进行自定义配置

这里实现一个比较通用的报错信息:

<div th:if="${param.error}">
  <i>You have entered invalid username/password.</i>
</div>

最终显示效果:

在这里插入图片描述

添加登出功能

这里 logout 也使用 spring boot 的默认方法,config 修改如下:

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeHttpRequests(configurer ->
                configurer
                        .anyRequest().authenticated()
                ).formLogin(form ->
                    form
                            .loginPage("/showLoginPage")
                            .loginProcessingUrl("/authenticateUser")    // no controller request mapping for this
                            .permitAll()
                ).logout(LogoutConfigurer::permitAll);

        return httpSecurity.build();
    }

HTML 模板更新如下:

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

实现效果:

在这里插入图片描述

这里 CSS 修改了一下,不过主要核心内容还是一样的

用户 & 权限

下面会实现根据用户权限限制用户访问的功能

显示用户名和权限

spring security 会将当前用户的验证信息传导 view 层,获取方法如下:

<!DOCTYPE html>
<html
  lang="en"
  xmlns:th="http://www.thymeleaf.org"
  xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
>
  <body>
    <p>
      User: <span sec:authentication="principal.username"></span> <br /><br />
      Role(s): <span sec:authentication="principal.authorities"></span>
    </p>

    <script src="http://localhost:35729/livereload.js"></script>
  </body>
</html>

渲染结果:

在这里插入图片描述


在这里插入图片描述

根据权限限制访问

这里可以通过两步实现:

  1. 添加对应的 controller & view 层实现重定向功能

    ⚠️:这里用户已经登录成功了,所以对应的功能在 DemoController 中实现:

        @GetMapping("/leaders")
        public String showLeaders() {
            return "leaders";
        }
    

    随后就是更新 Home 页面中,添加重定向的功能:

    <p>
      <a th:href="@{/leaders}">Leadership Meeting</a>
      (Only for Manager peeps)
    </p>
    

    以及实现对应的 Leaders 页面:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
        <meta charset="UTF-8" />
        <title>Leaders</title>
      </head>
      <body>
        <h2>Leaders</h2>
    
        <hr />
    
        <p>Page only available for Manager role</p>
    
        <a th:href="@{/}">Back to Home Page</a>
    
        <script src="http://localhost:35729/livereload.js"></script>
      </body>
    </html>
    
  2. 在 security config 中限制用户的访问权限

    实现如下:

        @Bean
        public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
            httpSecurity.authorizeHttpRequests(configurer ->
                    configurer
                            .requestMatchers("/").hasRole("EMPLOYEE")
                            .requestMatchers("/leaders/**").hasRole("MANAGERS")
                            .requestMatchers("/systems/**").hasRole("ADMIN")
                            .anyRequest().authenticated())
                    .formLogin(form ->
                        form
                                .loginPage("/showLoginPage")
                                .loginProcessingUrl("/authenticateUser")    // no controller request mapping for this
                                .permitAll()
                    ).logout(LogoutConfigurer::permitAll)
                    ;
    
            return httpSecurity.build();
        }
    

    完成这一步后,只有有对应权限的用户可以访问对应的页面

    John 只有 EMPLOYEE 的权限,因此只能访问首页,而 mary 和 susan 有 MANAGERS 的权限,所以它们可以访问 leaders 下的资源

实现效果如下:

在这里插入图片描述

⚠️:同样的变化也可以加到 admin 权限和 system 页面上,这里就不重复了

拒绝访问页面

目前因为 spring 没有对相应的报错页面进行配置,因此当权限不够(403)时,会显示 whitelabel 页面。鉴于大多数用户并不能够了解 HTTP 状态码,显然这不是一个用户友好型的实现

重定向一个对应的报错页面的实现就能够很好的提升用户体验

这里的实现和自定义登录/登出页面相似,主要是在 exceptionHandling 添加对应的报错页面:

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeHttpRequests(configurer ->
                configurer
                        .requestMatchers("/").hasRole("EMPLOYEE")
                        .requestMatchers("/leaders/**").hasRole("MANAGER")
                        .requestMatchers("/systems/**").hasRole("ADMIN")
                        .anyRequest().authenticated())
                .formLogin(form ->
                    form
                            .loginPage("/showLoginPage")
                            .loginProcessingUrl("/authenticateUser")    // no controller request mapping for this
                            .permitAll())
                .logout(LogoutConfigurer::permitAll)
                .exceptionHandling(configurer ->
                        configurer.accessDeniedPage("/access-denied"))
                ;

        return httpSecurity.build();
    }

controller 的实现如下:

    @GetMapping("/access-denied")
    public String showAccessDenied() {
        return "access-denied";
    }

⚠️:这里的实现我也放在了 LoginController 下面……其实感觉这个 controller 应该重命名为 auth controller 比较好

HTML 模板实现如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>Access Denied</title>
  </head>
  <body>
    <h2>Access Denied - You are not ahtorized to access this resource.</h2>

    <a th:href="@{/}">Back to Home Page</a>

    <script src="http://localhost:35729/livereload.js"></script>
  </body>
</html>

最终效果:

在这里插入图片描述

根据权限显示用户信息

目前的首页显示时完全一致的,不过对于 EMPLOYEE 权限的用户显示无法访问的页面,意义不是很大

这时候可以使用 spring security 提供的 sec:authorize="hasRole('ROLE')" 语法:

<p sec:authorize="hasRole('MANAGER')">
  <a th:href="@{/leaders}">Leadership Meeting</a>
  (Only for Manager peeps)
</p>

<p sec:authorize="hasRole('ADMIN')">
  <a th:href="@{/systems}">System Meeting</a>
  (Only for ADMIN peeps)
</p>

效果如下:

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

目前的项目结构如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/793719.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

构造函数的初始化列表,static成员,友元,内部类【类和对象(下)】

P. S.&#xff1a;以下代码均在VS2022环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;LiUEEEEE                        …

2-31 基于matlab的微表情识别

基于matlab的微表情识别。通过gabor小波提取表情特征&#xff0c;pca进行降维&#xff0c;ELM分类器训练&#xff0c;然后选择待识别的微表情&#xff0c;提取特征后输入训练好的模型进行分类&#xff0c;识别结果由MATLAB的GUI输出。程序已调通&#xff0c;可直接运行。 2-31 …

Tomcat多实例

一、Tomcat多实例 Tomcat多实例是指在同一台服务器上运行多个独立的tomcat实例&#xff0c;每个tomcat实例都具有独立的配置文件、日志文件、应用程序和端口&#xff0c;通过配置不同的端口和文件目录&#xff0c;可以实现同时运行多个独立的Tomcat服务器&#xff0c;每个服务…

Fastjson2使用JSONOObject或者mao转换为JSON字符串时丢失Null值字段

最近在工作中发现问题fastJson转换为JSONString时丢失值为null的问题特此解决。 public class test001 {public static void main(String[] args) {JSONObject jsonObject new JSONObject();jsonObject.put("foo1", "bar");jsonObject.put("foo2&quo…

19. 地址转换

地址转换 题目描述 Excel 是最常用的办公软件。每个单元格都有唯一的地址表示。比如&#xff1a;第 12 行第 4 列表示为&#xff1a;"D12"&#xff0c;第 5 行第 255 列表示为"IU5"。 事实上&#xff0c;Excel 提供了两种地址表示方法&#xff0c;还有一…

代码随想录第50天|单调栈

739. 每日温度 参考 思路1: 暴力解法 思路2: 单调栈 使用场合: 寻找任一个元素的右边或者左边第一个比自己大或者小的元素位置, 存放的是遍历过的元素 记忆: 单调栈是对遍历过的元素做记录, 一般是对栈顶的元素 nums[mystack.top()] 做赋值操作的 如果想找到右边的元素大于左…

Efficient Estimation of Word Representations in Vector Space论文笔记解读

基本信息 作者TomasMikolovdoi10.48550发表时间2013期刊ICLR网址http://arxiv.org/abs/1301.3781 研究背景 1. What’s known 既往研究已证实 前馈神经网络语言模型(NNLM) 循环神经网络语言模型(RNNLM) 2. What’s new 创新点 Word2vec有两种模型&#xff1a;CBOW和Skip-gr…

【区块链 + 智慧政务】一体化政务数据底座平台 | FISCO BCOS应用案例

为进一步贯彻落实《全国一体化政务大数据体系建设方案》、《中共中央国务院关于构建数据基础制度更好发挥 数据要素作用的意见》精神&#xff0c;一体化政务数据底座平台结合相应城市的数字经济现状基础、当前任务及未来发展 战略&#xff0c;规划建设数据底座&#xff0c;持续…

Qt QWebSocket网络编程

学习目标&#xff1a;Qt QWebSocket网络编程 学习前置环境 QT TCP多线程网络通信-CSDN博客 学习内容 WebSocket是一种通过单个TCP连接提供全双工通信信道的网络技术。2011年&#xff0c;IETF将WebSocket协议标准化为 RFC6455&#xff0c;QWebSocket可用于客户端应用程序和服…

社区团购小程序源码系统 带完整的安装代码以及搭建部署教程

系统概述 在这个数字化时代&#xff0c;线上活动成为了连接用户与组织者的桥梁。为了满足不同场景的需要&#xff0c;开发一个灵活、可定制的在线活动报名表单小程序显得尤为重要。本文将深入介绍一个自定义在线活动报名表单小程序的源码系统&#xff0c;并提供详细的搭建部署…

【JavaScript 算法】快速排序:高效的排序算法

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;通过分治法将数组分为较小的子数组&#xff0c;递归地排序子数组。快速排序通常…

vue使用quill编辑器自定义附件上传方法,并根据上传附件名称生成链接

1、附件上传 需求&#xff1a; 在编辑器中上传word,pdf,excel等附件后&#xff0c;能根据上传附件的名称生成link链接&#xff0c;在展示页面能实现点击链接下载或预览附件&#xff0c;效果图如下: 实现方法&#xff1a; quill编辑器自身带有link&#xff0c;但不满足需求&…

Java---SpringBoot详解二

勤奋勤劳铸梦成&#xff0c; 晨曦微露起长征。 汗水浇灌花似锦&#xff0c; 寒窗苦读岁月明。 千锤百炼心如铁&#xff0c; 万里征途志不倾。 持之以恒终有日&#xff0c; 功成名就笑谈中。 目录 一&#xff0c;统一响应结果 二&#xff0c;三层架构 三&#xff0c;分层解耦 四…

基于html开发的在线网址导航在线工具箱源码

基于html开发的在线网址导航在线工具箱源码&#xff0c;将全部文件复制到服务器&#xff0c;入口文件是index.html 如需修改网址&#xff0c;可修改index.html 如需修改关于页面&#xff0c;可修改about里面的index页面 源码下载&#xff1a;https://download.csdn.net/down…

存储实验:Linux挂载iscsi硬盘与华为OceanStor创建LUN全流程

目录 目的环境规划实验实验流程Centos配置0. 关闭防火墙1. 设置网卡信息2. 配置路由3. iscsiadm连接存储 iSCSI LUN创建&#xff08;以华为OceanStor为例&#xff09;验证1. 验证是否成功2. 开启自动挂载 目的 实现Linux连接iscsi硬盘&#xff0c;同时实现开机自启挂载 环境规…

综合实验作业

node01&#xff1a;192.168.175.146 node02&#xff1a;192.168.175.147 【node01】 node01 与 node02 防火墙在本实验中都需要放行的服务&#xff1b; [rootlocalhost ~]# firewall-cmd --permanent --add-servicedns success [rootlocalhost ~]# firewall-cmd --permanent -…

实变函数精解【3】

文章目录 点集求导集 闭集参考文献 点集 求导集 例1 E { 1 / n 1 / m : n , m ∈ N } 1. lim ⁡ n → ∞ ( 1 / n 1 / m ) 1 / m 2. lim ⁡ n , m → ∞ ( 1 / n 1 / m ) 0 3. E ′ { 0 , 1 , 1 / 2 , 1 / 3 , . . . . } E\{1/n1/m:n,m \in N\} \\1.\lim_{n \rightar…

PGCCC|【PostgreSQL】PCA认证考试大纲#postgresql认证

PostgreSQL Certified Associate|PCA&#xff08;初级&#xff09; 学员将学会安装、创建和维护PostgreSQL数据库。学完后&#xff0c;学员可以从事PostgreSQL数据库的数据操作和管理等工作。 获证途径 参加PostgreSQL培训再考试 考试为上机考试。 PostgreSQL PCA培训考试课…

“金山-讯飞”杯2024年武汉理工大学程序设计竞赛 A. Mobiusp败走***(思维题-点双连通分量、连通性)

题目 思路来源 官方题解 题解 手玩发现&#xff0c;能换的话&#xff0c;当且仅当.和1在一个环里&#xff0c;而这就是点双连通分量 所以最优策略是先把.换到(x,y)的位置&#xff0c;然后判断.和1在不在一个环里 也就是&#xff1a; 1. 判断删掉1时&#xff0c;.和(x,y)联…

VSCode上通过C++实现单例模式

单例模式实际上就是为了确保一个类最多只有一个实例&#xff0c;并且在程序的任何地方都可以访问这个实例&#xff0c;也就是提供一个全局访问点&#xff0c;单例对象不需要手动释放&#xff0c;交给系统来释放就可以了&#xff0c;单例模式的设计初衷就是为了在整个应用程序的…