【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

这篇具有很好参考价值的文章主要介绍了【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 🦁 认证前的工作

本次操作是基于SpringBoot项目的,使用Mybatis-Plus作为ORM框架,具体创建流程不再一一阐述。

1. 添加依赖

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

2. 创建数据库表(数据自行添加)

CREATE TABLE `users` (
  `uid` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;

3. 编写用户实体类

@Data
public class Users {
    private Integer id;
    private String username;
    private String password;
    private String phone;
}

4. 编写Dao接口

public interface UsersMapper extends BaseMapper<Users> {
}

5. 在启动类中添加 @MapperScan 注解

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.lion.mysecuritydemo.mapper")
public class MysecuritydemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MysecuritydemoApplication.class, args);
    }

}

6. 继续添加各种包

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

二. 🦁 自定义逻辑认证原理—UserDetailsService

在项目中,认证逻辑一般是通过自定义实现的,将实现了UserDetailsService 接口的实现类放入Spring容器中,即可实现自定义逻辑认证。

实现UserDetailsService接口必须重写 loadUserByUsername方法,该方法定义了具体的认证逻辑,参数 username 是前端传来的用户名,我们需要根据传来的用户名查询到该用户(一般是从数据库查询),并将查询到的用户封装成一个UserDetails对象,该对象是Spring Security提供的用户对象,包含用户名、密码、权限。Spring Security会根据UserDetails对象中的密码和客户端提供密码进行比较。相同则认证通过,不相同则认证失败,详细流程如下图:

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

三. 🦁 数据库认证

数据库认证是最常用的,我们现在来看看数据库认证应该怎么写?

其实就是按我们上面说的自定义一个MyUserDetailsService类,并且实现UserDetailsService接口,将其放入Spring容器中,如下:

@Service
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UsersMapper usersDao;

    /**
     * 自定义认证逻辑(现在是数据库认证)
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//        1. 查询用户
        QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);
        Users users = usersDao.selectOne(wrapper);

        if (users == null){
            return  null;
        }
//        2. 封装成UserDetails对象
        UserDetails userDetails = User.withUsername(users.getUsername())
                .password(users.getPassword())
                .authorities("admin")               //授权操作
                .build();

        return userDetails;
    }
}

四. 🦁 密文加密操作

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

在实际开发中,为了数据安全性,在数据库中存放密码时不会存放原密码,而是会存放加密后的密码。而用户传入的参数是明文密

码。此时必须使用密码解析器才能将加密密码与明文密码做比对。Spring Security中的密码解析器是 PasswordEncoder 。

Spring Security要求容器中必须有 PasswordEncoder 实例,Spring Security官方推荐的密码解析器是 BCryptPasswordEncoder

我们在security配置类中加入如下一个方法即可:

  @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();

    }

五. 🦁自定义表单登录

虽然Spring Security给我们提供了登录页面,但在实际项目中,更多的是使用自己的登录页面。Spring Security也支持用户自定义登

录页面。用法如下:

1. 编写自定义页面

<!doctype html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
    <link href="/css/styles.css" rel="stylesheet" >
</head>
<body>
<div class="htmleaf-container">
    <div class="wrapper">
        <div class="container">
            <h1>Welcome</h1>
            <form class="form" action="/login" method="post">
                <input type="text" placeholder="用户名" name="username">
                <input type="password" placeholder="密码" name="password">
                <button type="submit" id="login-button">登录</button>
            </form>
        </div>
        <ul class="bg-bubbles">
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
</div>
</body>
</html>

2. 在Spring Security配置类自定义登录页面

在Spring Security配置类里继承WebSecurityConfigurerAdapter类,重写protected void configure(HttpSecurity http) 方法,如下:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
    //        自定义表单登录
        http.formLogin()
                .loginPage("/login.html")      // 自定义登录页面
                .usernameParameter("username") // 表单中的用户名项
                .passwordParameter("password") // 表单中的密码项
                .loginProcessingUrl("/login")  // 登录路径,表单向该路径提交,提交后自动执行UserDetailsService的方法
                .successHandler(new MyLoginSuccessHandler())               // 登录成功后跳转的路径
                .failureHandler(new MyLoginFailureHandler());              // 登录失败后跳转的路径
    }

这里使用的认证成功和失败跳转的处理方式是编写自定义成功和失败处理器(个人认为这个方法比较常用),因为登录成功后,如果除了跳转页面还需要执行一些自定义代码时,如:统计访问量,推送消息等操作时,可以自定义登录成功处理器。

3. 配置登录成功跳转处理器

public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//        1. 拿到登录用户信息
        UserDetails principal = (UserDetails) authentication.getPrincipal();


//      做一些需要的事情




//        2. 重定向回到主页
        response.sendRedirect("/main");


    }
}

4. 配置登录失败跳转处理器

public class MyLoginFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
         System.out.println("记录失败日 志...");
         response.sendRedirect("/fail");
    }
}

5. 编写退出登录跳转处理器

public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("清除一些数据...");
        response.sendRedirect("/login.html");
    }
}

6. 编写退出登录跳转配置

在系统中一般都有退出登录的操作。退出登录后,Spring Security进行了以下操作:

  • 清除认证状态

  • 销毁HttpSession对象

  • 跳转到登录页面

在Spring Security中,退出登录的写法如下:在security配置类里编写:

//        退出登录配置
        http.logout()
                .logoutUrl("/logout")                 // 退出登录路径
//                .logoutSuccessUrl("/login.html")     // 退出登录后跳转的路径
                .logoutSuccessHandler(new MyLogoutSuccessHandler())
                .clearAuthentication(true)          //清除认证状态,默认为true
                .invalidateHttpSession(true);      // 销毁HttpSession对象,默认为true

六. 🦁 关闭csrf防护

CSRF:

跨站请求伪造,通过伪造用户请求访问受信任的站点从而进行非法请求访问,是一种攻击手段。 Spring Security

为了防止CSRF攻击,默认开启了CSRF防护,这限制了除了GET请求以外的大多数方法。我们要想正常使用Spring Security需要突破CSRF防护。

我们这里直接关闭csrf防护即可,在security配置类添加如下代码:

http.csrf().disable();

到这里认证工作就全部完成啦,现在来完成授权工作的编写!!!

——————over————————

七. 🦁 授权_RBAC

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

授权即认证通过后,系统给用户赋予一定的权限,用户只能根据权限访问系统中的某些资源。

Resource-Based Access Control

基于资源的访问控制,即按资源(或权限)进行授权。比如在企业管理系统中,用户必须 具有查询报表权限才可以查询企业运营报

表。逻辑为:

if(主体.hasPermission("查询报表权限")){
 查询运营报表
}

这样在系统设计时就已经定义好查询报表的权限标识,即使查询报表所需要的角色变化为总经理和股东也不需要修改授权代码,系统

可扩展性强。该授权方式更加常用。

八. 🦁 权限表设计

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

用户角色,角色权限都是多对多关系,即一个用户拥有多个角色,一个角色属于多个用户;一个角色拥有多个权限,一个权限属于多

个角色。这种方式需要指定用户有哪些角色,而角色又有哪些权限。

如:

张三拥有总经理的角色,而总经理拥有查询工资、查询报表的权限,这样张三就拥有了查询工资、查询报表的权限。这样管理用户时只需管理少量角色,而管理角色时也只需要管理少量权限即可。
【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

我们在原有的Users表上,再添加角色表和权限表:

// 角色
@Data
public class Role {
    private String rid;
    private String roleName;
    private String roleDesc;
}
// 权限
@Data
public class Permission {
    private String pid;
    private String permissionName;
    private String url;
}

并且在UsersDao接口添加findPermissionByUsername方法。

// 根据用户名查询权限
List<Permission> findPermissionByUsername(String username);

这个方法设计五表查询,需要自定义编写sql语句:

SELECT DISTINCT
		permission.pid,permission.permissionName,permission.url
	FROM
		users
		LEFT JOIN users_role ON users_role.uid = users.uid
		LEFT JOIN role ON role.rid = users_role.rid
		LEFT JOIN role_permission ON role_permission.rid = role.rid
		LEFT JOIN permission ON role_permission.pid = permission.pid
	WHERE
		username = #{username}

九. 🦁 修改认证逻辑,认证成功后给用户授权

@Service
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UsersMapper usersDao;

    /**
     * 自定义认证逻辑(现在是数据库认证)
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//        1. 查询用户
        QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username", username);
        Users users = usersDao.selectOne(wrapper);

        if (users == null){
            return  null;
        }
//        2. 查询用户权限
        List<Permission> permissions = usersDao.findPermissionByUsername(username);
        // 将自定义权限集合转为Security的权限类型集合
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (Permission permission : permissions) {
            grantedAuthorities.add(new SimpleGrantedAuthority(permission.getUrl()));
        }
//        3. 封装成UserDetails对象
        UserDetails userDetails = User.withUsername(users.getUsername())
                .password(users.getPassword())
                .authorities(grantedAuthorities)               //授权操作
                .build();

        return userDetails;
    }
}

十. 🦁 设置访问控制的三种方式

【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?

在给用户授权后,我们就可以给系统中的资源设置访问控制,即拥有什么权限才能访问什么资源。

1. 准备工作

编写控制器类,添加控制器方法资源

@RestController
public class MyController {
    @GetMapping("/reportform/find")
    public String findReportForm() {
        return "查询报表";
   }
    @GetMapping("/salary/find")
    public String findSalary() {
        return "查询工资";
   }
    @GetMapping("/staff/find")
    public String findStaff() {
        return "查询员工";
   }
}

2. 配置类设置访问控制

修改Security配置类:

// 权限拦截配置
http.authorizeRequests()
    .antMatchers("/login.html").permitAll() 			//表示任何权限都可以访问
    .antMatchers("/reportform/find").hasAnyAuthority("/reportform/find") 		// 给资源配置需要的权限
    .antMatchers("/salary/find").hasAnyAuthority("/salary/find")
    .antMatchers("/staff/find").hasAnyAuthority("/staff/find")
           .anyRequest().authenticated();  				//表示任何请求都需要认证后才能访问

3. 自定义访问控制逻辑

如果资源数量很多,一条条配置需要的权限效率较低。我们可以自定义访问控制逻辑,即访问资源时判断用户是否具有名为该资源

URL的权限。

@Service
public class MyAuthorizationService {
    // 自定义访问控制逻辑,返回值为是否可以访问资源

    public boolean hasPermission(HttpServletRequest request, Authentication authentication){
//        获取认证的用户
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
//        获取登录用户的权限
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
//        获取请求的URL路径
        String uri = request.getRequestURI();
//        将路径封装为权限对象
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(uri);
        return  authorities.contains(grantedAuthority);
    }

}

4. 注解设置访问控制

除了配置类,在SpringSecurity中提供了一些访问控制的注解。这些注解默认都是不可用的,需要开启后使用。

有两个,一个**@Secured**,因为使用麻烦,这里不细说。

我们来说说**@PreAuthorize**。该注解可以在方法执行前判断用户是否具有权限

① 在启动类开启注解使用

在启动类上方添加:

@EnableGlobalMethodSecurity(prePostEnabled = true)

② 在控制器方法上添加注解

@PreAuthorize("hasAnyAuthority('/reportform/find')")
@GetMapping("/reportform/find")
public String findReportForm() {
    return "查询报表";
}

到这里,一个权限表就完成啦!!!文章来源地址https://www.toymoban.com/news/detail-409783.html

到了这里,关于【Spring Security】| 从0到1编写一个权限认证 | 学会了吗?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最长递增子序列问题(你真的会了吗)

    目录 一.最长递增子序列问题I 二.最长递增子序列问题II 三. 最长递增子序列问题III 1.对应牛客网链接 最长上升子序列(一)_牛客题霸_牛客网 (nowcoder.com) 2.题目描述:  3.解题思路 1.首先我们分析题意:最长递增子序列拆:要递增的,还是序列,不一定连续 ,要长度最长的。

    2024年02月15日
    浏览(44)
  • 【数据库】数据库绪论,你都会了吗

    数据(Data) Q: 什么是数据 A: 数据(Data)是数据库中存储的基本对象。数据包括数字、文字、图形、图像、音频、视频、学生的档案记录等 数据库(Database) Q: 什么是数据库 A: 数据库(Database)是长期储存在计算机内、有组织的、可共享的大量数据的集合。 Q: 数据库

    2024年01月16日
    浏览(40)
  • 【Springboot】| 阿里云发送短信验证码,你会了吗?

    专栏 名字 🔥Elasticsearch专栏 es 🔥spring专栏 spring开发 redis专栏 redis学习笔记 🔥项目专栏 项目集锦 修bug专栏 bug修理厂 狮子之前发了一篇《邮箱发送验证码,你会了吗?》,很快上了热度榜单,但是那篇文章只是简单介绍了如何接收验证码的流程以及安利了一个接收验证码的

    2024年02月08日
    浏览(31)
  • js数组去重(9种方法),你都会了吗?

    以下共有九种数组去重的方式和详解(包含对象数组去重): 1.利用Array.from(new Set)去重: 效果: 2.利用includes去重 效果 3.利用map去重  效果: 4.利用indexOf去重 效果: 5. 利用单层for循环去重  效果: 6.利用双层for循环去重 效果:  7.利用递归去重  效果: 8.利用Array.filter和

    2024年02月12日
    浏览(32)
  • springboot整合security,mybatisPlus,thymeleaf实现登录认证及用户,菜单,角色权限管理

    本系统为springboot整合security,mybatisPlus,thymeleaf实现登录认证及用户,菜单,角色权限管理。页面为极简模式,没有任何渲染。 源码:https://gitee.com/qfp17393120407/spring-boot_thymeleaf 架构截图 此处以用户表为例,其他表数据可在源码获取。 用户表 共用属性 共用属性自动填充配置

    2024年02月07日
    浏览(40)
  • Spring Security认证研究

     1.统一认证  认证通过由认证服务向给用户颁发令牌,相当于访问系统的通行证,用户拿着令牌去访问系统的资源。  2.单点登录,对于微服务项目,因为包含多个模块,所以单点登录就是使得用户可以在认证一次的情况下就可以访问所有的拥有对应权限的服务。 3.第三方认

    2023年04月08日
    浏览(27)
  • 软件测试|使用Python读写yaml文件,你会了吗?

    简介 YAML(YAML Ain\\\'t Markup Language)是一种可读的数据序列化格式,它常用于配置文件和数据交换。Python 提供了许多库来处理 YAML 文件。在本文中,我们将探讨如何使用 PyYAML 库来读取和写入 YAML 文件,以及提供一些示例来说明其用法。 环境准备 在读写yaml文件之前,我们需要先

    2024年01月16日
    浏览(32)
  • spring security认证授权流程

    认证和授权是任何安全体系中的两个主要功能,而在现代Web开发中,Spring Security是最受欢迎和广泛使用的安全框架之一。在本篇文章中,我们将全面介绍Spring Security的认证和授权机制,并提供详细的步骤和示例代码。  一、认证(Authentication) 认证的主要目的是验证用户的身

    2024年02月15日
    浏览(29)
  • Spring security权限管理

    主要内容 一、Spring Security简介 1.概括 ​ Spring Security是一个高度自定义的 安全框架 。利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作。 ​ 使用Spring Secruity的原因有很多,但大部分都是发现了javaEE的Servlet规范或

    2024年02月03日
    浏览(26)
  • 软件测试|如何实现字典的键值互换,你会了吗?

    简介 在Python中,字典是一种非常有用的数据结构,它将数据存储为键值对,并且键必须是唯一的。有时候,我们可能需要将字典的键和值互换,以便查找或操作数据更加方便。本文将详细介绍如何在Python中实现字典键值的互换操作。 字典的基本概念 首先,让我们快速回顾一

    2024年01月21日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包