Spring Security安全管理

news2024/11/14 4:14:25

目录

一.添加依赖

效果

二.设置配置文件

认证

1.密码生成器 BCryptPasswordEncoder

配置文件中

2.inMemoryAuthentication内存认证方法

授权

效果

登录

效果

三.UserDetailsService认证授权方式

新建数据库

实体类

Role

User

接口

实现类

配置文件

效果

四.注销

配置

未使用前

使用后

前端页面

1.动态参数

2.sec:authorize


 

一.添加依赖

方法一:可以在新建项目时添加依赖

7e4c736f7ab54ceeb95302223f795dd5.png

方法二:

在pom文件中添加依赖

 

<groupId>com.example</groupId>
    <artifactId>Security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Security</name>

效果

进入任何页面前,都会进入系统登录页面

0e5ca9feefe44c399ebb45c6130b8fac.png

二.设置配置文件

认证

1.密码生成器 BCryptPasswordEncoder

encode,密码编译的方法,可以将密码编码

d592cc5f5d4243fcb0e798a3956e01c9.png

matches,比较加密后密码和初始密码是否相同。

public class Time {

    public static void main(String[] args) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
//        加密
        String i="123456";
        String encode = encoder.encode(i);
        System.out.println(i);
        System.out.println(encode);
        matches(i,encode);
    }

    private static Boolean matches(String i, String encode) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(i, encode);
        System.out.println(matches);
        return matches;
    }
}

7936c1374be14d98bfb7de3bab7cbd66.png

配置文件中

   

 @Autowired//自动调用该方法
    public void configuree(AuthenticationManagerBuilder auth) throws Exception {
        //设置密码编译器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encode1 = encoder.encode("111111");
        String encode2 = encoder.encode("222222");

2.inMemoryAuthentication内存认证方法

此时创建了用户名和密码,并且为用户附上权限。

 

//inMemoryAuthentication内存认证方法
        //创建了用户名和密码,还附上了权限  (此时用户名和密码全部写死)
        //用户1
        auth.inMemoryAuthentication()
                .passwordEncoder(encoder)
                .withUser("zhangsan")
                .password(encode1)
                .roles("common");
        //用户2
        auth.inMemoryAuthentication()
                .passwordEncoder(encoder)
                .withUser("lisi")
                .password(encode2)
                .roles("vip");

授权

需要给页面授权,不同的授权有不同的权限用户访问,此时一旦用户访问到权限不够的页面,则会出现403错误。

//2.再完成页面的授权
    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        //过滤请求
        httpSecurity.authorizeHttpRequests(authorize -> {
            //系统默认寻找static包下,所以直接写包下的路径
            //完全放开的接口
            authorize.requestMatchers("/userLogin", "/login", "/login/**","/index ","/img/css/**", "/login/img/**", "/detail/common/*")
                    .permitAll();
            //需要授权才能登录的页面
            authorize.requestMatchers("/detail/vip/*")//vip用户必须登录和授权
                    // .hasAnyRole("vip,admin")多权限授权
                    .hasAnyRole("vip")//授权
                    .anyRequest()//任何请求
                    .authenticated();//都要认证(登录)
        });
        return httpSecurity.build();
    }

效果

进入页面:

e0fe02021f5b44bf8ecb9ce0fe72a300.png

访问普通页面:

5ddce06590f248538bb817260984672d.png

访问vip页面时:

e479c3a7b71047a694c3452f80109fd7.png

登录

vip页面进入时必须要登陆vip账号,所以游客和普通用户不能查看vip中的内容,想要查看时,就必须登录才能查看。

 

.formLogin(form->form
                        //登录访问路径 该地址必须和登录页面表单提交的地址一致
                        .loginProcessingUrl("/userLogin")
                        //登陆表单输入的用户名的name
                        .usernameParameter("name")
                        //登陆表单输入的密码的name
                        .passwordParameter("pwd")
                        //登录页面
                        .loginPage("/login")
                        //(有需要则写)跳转注意,不会跳转到之前请求的路径
                        //登录成功后跳转到路径
                        //.defaultSuccessUrl("/userLogin")
                        //登录失败后跳转路径
                        //.failureForwardUrl("/login")
                );

2fd9790baafa47da9bb3e5b89add33ec.png

效果

普通用户:

26d663199bb9486fa1a9da4dd8d2c9e2.png

可以进入普通页面

545a1437eebb4df3a126c8bf0bbebbcd.png

不能进入vip页面

4e1eb059f00c425890d32d64ea20ba1e.png

vip用户

95c92176efb74329a7e7f2eaedf3bcc2.png

可以进入普通页面

0f8310b3e6614ea4959f71c1e47e5828.png

也可以进入vip页面

8743f820748c4bb3a3eb1e8d48a6cb28.png

三.UserDetailsService认证授权方式

这种方式将会连接数据库,将登录用户名和密码都存在数据库中。

新建数据库

create database security;
use security;
# 用户表
create table user
(
    id                    int(11) auto_increment primary key,
    username              varchar(32)  default null,
    #因为密码需要进行加密,加密后存入数据库,所以密码位数要长
        password              varchar(255) default null,
    enabled               tinyint(1)   default null,
    accountNonExpired     tinyint(1)   default null,
    accountNonLocked      tinyint(1)   default null,
    credentialsNonExpired tinyint(1)   default null
);
# 角色表
create table role
(
    id      int(11) auto_increment primary key,
    name    varchar(32) default null,
    name_zh varchar(32) default null
);
# 用户角色表
create table user_role
(
    id  int(11) auto_increment primary key,
    uid int(11) default null,
    rid int(11) default null
);

# 插入用户数据
insert into user
values (1, 'lisi', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', 1, 1, 1, 1),
       (2, 'admin', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', 1, 1, 1, 1),
       (3, 'zhangsan', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', 1, 1, 1, 1);

# 插入角色数据
insert into role
values (1, 'ROLE_common', '一般用户'),
       (2, 'ROLE_admin', '系统管理员'),
       (3, 'ROLE_vip', 'vip用户');

#插入用户角色数据
insert into user_role
values (1, 1, 1),
       (2, 2, 2),
       (3, 3, 3);

实体类

Role

package com.example.security.entity;

import lombok.Data;

@Data
public class Role{

    private Integer id;
   
    private String name;
   
    private String nameZh;

}


 

User

package com.example.security.entity;

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Data
//供用户信息的核心接口。接口实现仅仅存储用户的信息。后续会将该接口提供的用户信息封装到认证对象Authentication中去
public class User implements UserDetails {
    private Integer id;

    private String username;

    private String password;

    private Boolean enabled;

    private Boolean accountNonExpired;

    private Boolean accountNonLocked;

    private Boolean credentialsNonExpired;

    private List<Role> roles;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        // 使用 Stream API 遍历用户的角色列表
        roles.stream().forEach(role -> {
            // 将每个角色转换为 GrantedAuthority 对象
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
            // 添加到 grantedAuthorities 列表中
            grantedAuthorities.add(grantedAuthority);
        });

        return grantedAuthorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        // 判断用户帐号是否过期
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        // 判断用户帐号是否被锁定
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // 判断用户凭证是否过期
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        // 判断用户是否启用
        return enabled;
    }
}

接口

//日期2024/3/20
package com.example.security.mapper;

import com.example.security.entity.Role;
import com.example.security.entity.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
@Mapper
public interface UserDao {
    //根据用户名查询用户
    User loadUserByUsername(String username);
    //根据用户id查询角色
    List<Role> getRolesByUid(Integer uid);
}

实现类
//日期2024/3/20
package com.example.security.service;

import com.example.security.entity.User;
import com.example.security.mapper.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

@Component
public class MyUserDetailService implements UserDetailsService {
    private final UserDao userDao;

    @Autowired
    public MyUserDetailService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.loadUserByUsername(username);
        if (ObjectUtils.isEmpty(user)) throw new RuntimeException("用户不存在");
        user.setRoles(userDao.getRolesByUid(user.getId()));
        return user;
    }

    private String encoder(String password) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        boolean b = encoder.matches("123456", password);
        System.out.println(b);
        return password;
    }
}

配置文件

只需要改变认证部分其他部分不变

  

  @Autowired
    public UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        //创建一个 PasswordEncoder 接口的实现类,并将其设置为密码加密器
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

效果

b7d8d9132bac4f708c5a7f8bd163145f.png

83119ac021ee4324ab3b520c4d2c2337.png

3b17e605281a480e8fcba7c862aeeaa5.png

四.注销

这里的注销并非返回登录页面,而是该用户的所有信息清空,需要重新登陆。

  • 校验HTTP Session
  • 清除RememberMe认证信息
  • 清理 SecurityContextHolder
  • 重定向到 /login

配置

 //logout必须是POST提交
                .logout(logout -> logout
                        //注销路径
                        .logoutUrl("/login")
                        //注销成功后跳转路径
                        .logoutSuccessUrl("/login")
                );

52d1adaf064c42fb9a3a8b7056804591.png

未使用前

06097e2cd3b74fc3abd0a4eb39540a03.png

点击注销,虽然进入登录页面,但是并未清除认证信息

25ab55b0ef7b4ed194aab163c5e7131c.png

不登陆,重新进入userList页面,信息仍旧存在。

d83edee7603d4a8093ddc5358780aac6.png

使用后

刚进入页面

12efe965e04f48f3881ba66fc6f325e6.png

登录后

4ca10c0ca8e84ef6bfb5e07d07b0ccd1.png

点击注销后

89561941a0db4fd49eef00e73ee85543.png

不登陆,重新进入userList页面

313dc4f703d94f88bcdb5546381e72c5.png

前端页面

1.动态参数

不同的包下可以有同样的页面命名,在控制层,为了方便,可以将路径设为动态参数,只需要写一个方法

1364305a6e9a4d64b8ff154a90a38851.png

将路径和页面分别设为参数type和path,根据点击的不同,传进的参数不同,最终包装好返回值即可。

 

@GetMapping("/detail/{type}/{path}")
    public String toDetail(@PathVariable("type") String type, @PathVariable("path") String path) {
        // 返回 detail 目录下的 type 类型的文件的路径
        return "detail/" + type + "/" + path;
    }

2.sec:authorize

类似于th:,使用在页面标签内部,authorize是用来判断普通权限的,通过判断用户是否具有对应的权限而控制其所包含内容的显示。

sec:authorize="isAnonymous()" //用户为游客则显示

sec:authorize="isAuthenticated()" //用户通过验证则显示

sec:authorize="hasRole('common')" //用户为common角色则显示

sec:authorize="hasAuthority('ROLE_vip')"//用户为ROLE_vip权限则显示

<h1 align="center">欢迎进入电影网站首页</h1>
<!--sec:authorize="isAnonymous()" 如果没有登录,此方法为ture,则显示-->
<div sec:authorize="isAnonymous()"><h2 align="center">游客您好,如果想查看电影<a th:href="@{/login}">请登录</a></h2>
</div>
<div sec:authorize="isAuthenticated()">
    <h2 align="center"><span sec:authentication="name" style="color: #007bff"></span>您好,您的用户权限为
        <span sec:authentication="principal.authorities" style="color:darkkhaki"></span>,您有权观看以下电影
    </h2>
    <form th:action="@{/login}" method="post"><input th:type="submit" th:value="注销"/></form>
</div>
<hr>
<!--根据权限不同,页面显示的内容不同-->
<div sec:authorize="hasAnyRole('ROLE_vip','ROLE_common')">
<h3>普通电影</h3>
    <ul>
        <li><a th:href="@{/detail/common/1}">飞驰人生</a></li>
        <li><a th:href="@{/detail/common/2}">夏洛特烦恼</a></li>
    </ul>
</div>
<div sec:authorize="hasAuthority('ROLE_vip')">
    <h3>VIP专享</h3>
    <ul>
        <li><a th:href="@{/detail/vip/1}">速度与激情</a></li>
        <li><a th:href="@{/detail/vip/2}">猩球崛起</a></li>
    </ul>
</div>

效果:

vip登录时

2e4eb30234004d02b332d85257e6d5f6.png

common登录时

ff3af2abe4bb4ae0ba230a5cda094ec5.png

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(AtCoder Beginner Contest 325) ---- D - Printing Machine -- 题解

目录 D - Printing Machine&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 代码实现&#xff1a; D - Printing Machine&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 打印一次后&#xff0c;需要充电一微秒后才能再次打印就可以看作每微妙只能打印一…

Vue3更新Package.json版本号

由于我之前已经更新过了&#xff0c;下面的方法提示我已经是最新的了&#xff0c;记录一下&#xff0c;过段时间在测试一下 npm install -g vue/clivue upgrade

Gitee删除自己本地仓库

1、打开自己的本地仓库 2、点击管理 3、选择删除仓库 4、将□的内容复制到⭕里

文件上传一-WEB攻防-PHP应用文件上传函数缺陷条件竞争二次渲染黑白名单JS绕过9

演示案例&#xff1a; PHP-原生态-文件上传-前后端验证PHP-原生态-文件上传-类型文件头验证PHP-原生态-文件上传-后缀黑白名单验证PHP-原生态-文件上传-解析配置&二次渲染PHP-原生态-文件上传-逻辑缺陷&函数缺陷 #学习前必读&#xff1a; 1、课前一定要明白&#xff1a…

nginx: [emerg] stream directive is duplicate in /etc/nginx/nginx.conf:56

背景&#xff1a; 在维护paas平台的时候发现一个web前端容器服务运行报错&#xff0c;提示如下&#xff1a; 问题分析&#xff1a; 根据日志的内容&#xff0c;发现是nginx.conf配置文件的stream模块配置存在问题导致的。需要查看一下nginx.conf配置文件的内容&#xff1a; 注…

LeetCode Python - 73. 矩阵置零

目录 题目描述解法方法一&#xff1a;数组标记方法二&#xff1a;原地标记 运行结果方法一方法二 题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;…

FFmpeg拉取RTSP流并定时生成10秒短视频

生成效果: 视频时长为10秒 生成格式为FLV 输出日志: 完整实现代码如下: 需要在Mac和终端先安装FFmpeg brew install ffmpeg CMake文件配置: cmake_minimum_required(VERSION 3.27) project(ffmpeg_open_stream) set(CMAKE_CXX_STANDARD 17)#头文件包目录 include_director…

可调恒流电子负载优点和应用

可调恒流电子负载是一种可以模拟真实负载的电子设备&#xff0c;它可以在电源电压和电流范围内提供恒定的电流或电压。这种设备在许多领域都有广泛的应用&#xff0c;如电力系统、通信设备、汽车电子、航空航天等。以下是可调恒流电子负载的优点和应用。 优点&#xff1a; 精确…

HarmonyOS NEXT应用开发之ArkWeb同层渲染

介绍 该方案展示了ArkWeb同层渲染&#xff1a;将系统原生组件直接渲染到前端H5页面上&#xff0c;原生组件不仅可以提供H5组件无法实现的一些功能&#xff0c;还能提升用户体验的流畅度 效果图预览 使用说明 进入页面即可看到同层渲染效果&#xff0c;Text&#xff0c;searc…

-bash: ./1.sh: /bin/bash^M: bad interpreter: No such file or directory解决方法

1、执行脚本 ./1.sh时报如下错误 -bash: ./1.sh: /bin/bash^M: bad interpreter: No such file or directory 2、在Windows编辑的脚本导入Linux系统中&#xff0c;执行报错问题 yum install -y dos2unix 3、或者本地安装 rpm -ivh /mnt/Packages/dos...... 4、然…

【战略前沿】丹麦正在建造一台英伟达人工智能超级计算机

【原文】Denmark is building an Nvidia AI supercomputer 【作者】Linnea Ahlgren 它将于今年上线&#xff0c;并以新的量子计算软件为特色。 过去一年最大的赢家——芯片制造商英伟达&#xff08;Nvidia&#xff09;和制药制造商诺和诺德&#xff08;Novo Nordisk&#xff0…

MCGS学习——用户管理

用户管理介绍 用户管理主要是为了实现触摸屏的安全操作&#xff0c;工业过程控制中&#xff0c;应该尽量避免由于人为的误操作所引发的故障或事故&#xff0c;而某些失误带来的后果是致命的&#xff1b;通过用户管理严格限制各类操作的权限&#xff0c;使不具备操作资格的人员…

LeetCode刷题日志-153.寻找旋转排序数组中的最小值

思路&#xff1a;总所周知二分的逻辑非常简单&#xff0c;难点在边界处理。这道题我说说自己的理解&#xff0c; 首先二分的根本是有序&#xff0c;只要有序就能二分&#xff0c;哪怕是部分有序&#xff08;这个是重点&#xff01;&#xff01;&#xff09; 我们先搞清楚题目中…

MyBatis:查询与连接池

一、查询 1、多表查询 尽量避免使用多表查询&#xff0c;尤其是对性能要求较高的项目。因为多表查询必然会导致性能变低。 例如&#xff1a;select *from ta运行需要10ms&#xff0c;select *from tb 运行也需要10s。但是&#xff0c;select *from ta left join tb on ta.xx…

【Web APIs】DOM节点

目录 1.节点操作 1.1DOM节点 1.2查找节点 1.2.1父节点查找 1.2.2子节点查找 1.2.3兄弟节点查找 1.3增加节点 1.4克隆节点 1.5删除节点 2.时间对象 2.1实例化 2.2时间对象方法 2.3时间戳 3.重绘和回流 1.节点操作 1.1DOM节点 DOM节点&#xff1a;DOM树中的每一个…

计算机网络:物理层中的数字传输系统全景概览解析

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

windows一键快速安装python方法

正常我们安装python的时候&#xff0c;需要先去下载python压缩包&#xff0c;然后再一步步安装&#xff0c;今天发现一个windows10下&#xff0c;一键安装python的方法&#xff1b; 电脑环境&#xff1a;windows10以上 安装方法&#xff1a; 0&#xff1a;在应用商店搜索pyt…

nodejs+vue反诈科普平台的设计与实现pythonflask-django-php

相比于以前的传统手工管理方式&#xff0c;智能化的管理方式可以大幅降低反诈科普平台的运营人员成本&#xff0c;实现了反诈科普平台的标准化、制度化、程序化的管理&#xff0c;有效地防止了反诈科普平台的随意管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能够…

flask_Restful数据解析参数设置

add_argument 方法参数详解 add_argument方法可以指定这个字段的名字&#xff0c;这个字段的数据类 型等&#xff0c;验证错误提示信息等&#xff0c;具体如下&#xff1a; default&#xff1a;默认值&#xff0c;如果这个参数没有值&#xff0c;那么将使用这个参数 指定的默认…

防外破警示灯:高压线“守护神”,照亮安全之路

近年来&#xff0c;随着城市施工建设项目不断增多&#xff0c;大型施工机械在输电通道内活动越来越频繁&#xff0c;线路外破隐患点大幅增多。一旦施工机械在作业过程中碰触到高压线&#xff0c;将会造成线路外破事故&#xff0c;严重威胁输电线路和施工人员的安全。 哪些境况下…