基于拦截器Interceptor实现简易权限控制及行为记录功能

news2024/10/6 4:11:10

一、业务需求

        使用拦截器(Interceptor),实现Controller中方法的权限控制,并记录访问行为。要求仅在Controller方法上加注解,就可以实现权限控制。具体为:

        1、拦截未登录用户的访问;

        2、拦截不具有权限用户的访问;

        3、用户访问成功,记录访问时间、设备等信息。

对拦截器还不了解的可以看我这一篇文章《Java拦截器(Interceptor)和过滤器(Filter)实例详解》

二、数据库设计 

        简单设计两个数据库,一个是用户表,一个是日志表。

用户表建表语句:

CREATE TABLE `user` (
  `user_id` varchar(255) NOT NULL,
  `user_role` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

存入两个模拟数据:

日志表建表语句:

CREATE TABLE `record` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `ip` varchar(255) DEFAULT NULL,
  `method` varchar(255) DEFAULT NULL,
  `browser` varchar(255) DEFAULT NULL,
  `time` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

里面数据差不多这个样子:

        接着是实体类,及其对应的Mapper,这里使用了lombok和Mybatis plus。

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Hao
 * @program: DockerTest
 * @description: 用户
 * @date 2023-10-23 15:22:36
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {

    // 模拟用户ID
    private String userId;
    // 模拟用户角色
    private String userRole;
}
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Hao
 * @program: DockerTest
 * @description: 访问记录
 * @date 2023-10-20 12:09:27
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("record")
public class Record {

    // 自增ID
    @TableId(type = IdType.AUTO)
    private Long id;

    // 访问IP
    private String ip;

    // 请求方式
    private String method;

    // 浏览器标识
    private String browser;

    // 访问时间
    private String time;
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hao.dockertest.po.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Hao
 * @program: DockerTest
 * @description: User Mapper
 * @date 2023-10-23 15:24:49
 */
@Mapper
public interface UserDAO extends BaseMapper<User> {
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hao.dockertest.po.Record;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Hao
 * @program: DockerTest
 * @description: Record Mapper
 * @date 2023-10-20 12:12:03
 */
@Mapper
public interface RecordDAO extends BaseMapper<Record> {
}

三、Service代码

        由于使用了Mybatis Plus,这里开发就简单很多

首先是UserService

import com.baomidou.mybatisplus.extension.service.IService;
import com.hao.dockertest.po.User;

/**
 * @author Hao
 * @program: DockerTest
 * @description: User接口
 * @date 2023-10-23 15:24:34
 */
public interface UserService extends IService<User> {

    // 检查用户对应的角色
    boolean checkUserRole(String userId, String userRole);
}

然后是其实现类,只有一个简单的校验用户角色和传入的角色是否相等。

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hao.dockertest.mapper.UserDAO;
import com.hao.dockertest.po.User;
import com.hao.dockertest.service.UserService;
import org.springframework.stereotype.Service;

/**
 * @author Hao
 * @program: DockerTest
 * @description: UserService实现类
 * @date 2023-10-23 15:25:48
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserDAO, User> implements UserService {

    /**
     * 检查用户对应的角色
     * @param userId 用户ID
     * @param userRole 要检查的角色
     * @return yes or no
     */
    @Override
    public boolean checkUserRole(String userId, String userRole) {

        User user = this.lambdaQuery().eq(User::getUserId, userId).one();
        if(user != null)    return userRole.equals(user.getUserRole()); // 返回要检查的用户角色是否和数据库中存储的角色对于
        return false;
    }
}

然后是日志接口和实现类,里面都没有东西,因为Mybatis plus帮我们做了。

import com.baomidou.mybatisplus.extension.service.IService;
import com.hao.dockertest.po.Record;

/**
 * @author Hao
 * @program: DockerTest
 * @description: Record接口层
 * @date 2023-10-20 12:12:42
 */
public interface RecordService extends IService<Record> {

}
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hao.dockertest.mapper.RecordDAO;
import com.hao.dockertest.po.Record;
import com.hao.dockertest.service.RecordService;
import org.springframework.stereotype.Service;

/**
 * @author Hao
 * @program: DockerTest
 * @description: RecordService实现类
 * @date 2023-10-20 12:25:56
 */
@Service
public class RecordServiceImpl extends ServiceImpl<RecordDAO, Record> implements RecordService {

}

接下来再弄两个工具类,一个是JWT工具类,用于生成token、验证token和解析token;还有一个是时间日期格式的(简单写下)。

import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.UUID;

/**
 * @author Hao
 * @program: DockerTest
 * @description: JWT工具
 * @date 2023-10-23 10:44:36
 */
@Slf4j
public class JWTUtil {

    private static long time = 1000*60*60*10;
    // 签名
    private static final String signature = "test";

    // 生产Token
    public static String createToken(String userName, String userID){
        JwtBuilder jwtBuilder = Jwts.builder();//构建JWT对象
        return jwtBuilder
                // Header
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                // payload
                .claim("userName",userName)
                .claim("userId", userID)
                // 设置有效期(毫秒单位)
                .setExpiration(new Date(System.currentTimeMillis()+time))
                .setId(UUID.randomUUID().toString())
                // signature
                .signWith(SignatureAlgorithm.HS256, signature)
                // compact拼接三部分header、payload、signature
                .compact();
    }

    // 验证Token
    public static Boolean checkToken(String token){
        if(token == null){
            return false;
        }
        try {
            JwtParser jwtParser = Jwts.parser();
            jwtParser.setSigningKey(signature).parseClaimsJws(token);
            return true;
        }catch (Exception e){
            // log.error("token失效");
            return false;
        }
    }

    // 解析Token
    public static String getTokenInfo(String token, String key){
        if(token == null || key == null)   return null;
        JwtParser parser = Jwts.parser();
        Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
        Claims payload = claimsJws.getBody();

        // 获取key对于的内容
        return payload.get(key).toString();
    }

}
import java.text.SimpleDateFormat;

/**
 * @author Hao
 * @program: DockerTest
 * @description: 时间格式工具
 * @date 2023-10-22 17:33:19
 */
public class MyTimeUtil {

    public static SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}

 四、Controller代码

        在写Controller之前,我们先要定义一个注解,这个注解可以加在方法上,指定某个方法需要什么角色,如果不会注解的可以看廖雪峰的官方网站--定义注解。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Hao
 * @program: DockerTest
 * @description: 需要某种角色注解
 * @date 2023-10-23 15:17:18
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {

    // 这个value就存需要什么角色
    String value() default "";
}

        然后就可以定义Controller了,这里我们模拟三个简单方法,分别是:登录,普通用户常规操作,管理员用户查询日志操作。

        1、登录操作,任何人都可以访问,根据用户ID生成token返回给前端,后续前端访问可以携带token访问;

        2、模拟普通用户常规功能:普通用户可以访问的功能;

        3、模拟管理员获取日志表单条记录功能:管理员用户根据日志ID查询某条记录,此操作仅管理员可访问。

import com.hao.dockertest.AOP.RequireRole;
import com.hao.dockertest.po.Record;
import com.hao.dockertest.service.RecordService;
import com.hao.dockertest.util.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author Hao
 * @program: DockerTest
 * @description: Record管理层
 * @date 2023-10-20 12:13:28
 */
@RestController
@RequestMapping
@Slf4j
public class RecordController {

    @Autowired
    private RecordService recordService;

    /**
     * 模拟用户登录功能(直接返回token,只是模拟)
     * @return token
     */
    @PostMapping("/login")
    public String login(){

        // return JWTUtil.createToken("张三", "20210919"); // 模拟返回登录成功token
        return JWTUtil.createToken("李四", "20210920"); // 模拟返回登录成功token
    }

    /**
     * 模拟普通用户常规功能
     * @return 返回Welcome
     */
    @GetMapping
    @RequireRole("common")
    public String getInfo(){

        return "Welcome!";
    }

    /**
     * 模拟管理员获取日志表单条记录功能
     * @param id 日志ID
     * @return 日志内容
     */
    @GetMapping("/getInfo/{id}")
    @RequireRole("admin")
    public String getRecord(@PathVariable Long id){

        if(id == null)  return "Id must not null!";
        Record record = recordService.getById(id);
        if(record == null)  return "Do not have this record!";

        return record.toString();
    }
}

        至此,我们的基本业务以及模拟完成,下面就需要定义我们的拦截器,实现拦截需求。

五、自定义拦截器

import com.hao.dockertest.AOP.RequireRole;
import com.hao.dockertest.po.Record;
import com.hao.dockertest.service.RecordService;
import com.hao.dockertest.service.UserService;
import com.hao.dockertest.util.JWTUtil;
import com.hao.dockertest.util.MyTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;


/**
 * @author Hao
 * @program: DockerTest
 * @description: 拦截器
 * @date 2023-10-21 21:13:27
 */
@Slf4j
@Configuration
public class MyInterceptor implements HandlerInterceptor {

    @Autowired
    private RecordService recordService;

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("拦截器前置处理 preHandle");

        // 验证token
        String token = request.getHeader("token");
        if(token == null || !JWTUtil.checkToken(token)){
            log.error("未登录或身份信息验证失败");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter printWriter = response.getWriter();
            printWriter.write("您还未登录或身份验证失败,请重新登录!");
            return false;
        }

        // 验证角色
        HandlerMethod method = (HandlerMethod) handler; // 此处仅是模拟,理论上应该先使用instanceof检验
        RequireRole requireRole = method.getMethodAnnotation(RequireRole.class); // 通过反射获取方法注解
        if(requireRole != null){
            String userId = JWTUtil.getTokenInfo(token, "userId");
            // 判断此用户是否有访问权限
            if(!userService.checkUserRole(userId, requireRole.value())) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter printWriter = response.getWriter();
                printWriter.write("您无权访问此功能!");
                log.error("用户{}非法访问,已成功拦截!", userId);
                return false;
            }
        }

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("拦截器后置处理 postHandle");

        // 从HttpServletRequest获取相关信息
        String ip = request.getRemoteAddr();
        if (ip.equals("0:0:0:0:0:0:0:1"))   ip = "127.0.0.1";
        String browser = request.getHeader("Sec-Ch-Ua-Platform");
        String httpMethod = request.getMethod();
        String time = MyTimeUtil.sdf.format(System.currentTimeMillis());

        // 日志存档
        Record record = new Record(null, ip, httpMethod, browser, time);
        recordService.save(record); // 访问日志记录

        // 从token中获取相关信息
        String token = request.getHeader("token");
        String userName = JWTUtil.getTokenInfo(token, "userName");
        String userId = JWTUtil.getTokenInfo(token, "userId");

        log.info("访问用户:{}-{},访问IP:{},访问时间:{},请求方式:{},访问设备:{}",userName, userId, ip, time, httpMethod, browser);

        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("拦截器完成后 afterCompletion");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

        在preHandle()方法中我们首先验证用户是否登录和用户token是否有效,如果无效,直接拦截,让用户登录;验证身份之后,我们还需要验证他的角色是否符合访问方法的要求,我们通过反射获取方法上的注解,并获取注解中的value,与我们数据库中存储的角色进行对比,如果符合要求则放行,如果不符合要求,则拦截访问,并提示用户权限不足。

        在postHandle()方法中,我们从HttpServletRequest中,获取到了用户的IP、浏览器类型、请求方式等信息,持久化到我们的数据库中。

        定义完我们自己的拦截器之后,还要将其配置到Spring MVC中才会生效。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Hao
 * @program: DockerTest
 * @description: Interceptor配置
 * @date 2023-10-21 21:26:18
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/login"); // 拦截所有请求,排除login
    }
}

        在上面的配置中,首先将我们自定义的拦截器addInterceptor,然后addPathPatterns指定了要拦截哪些路径,这里我们设置全部拦截,但是还要通过excludePathPatterns放行/login,不然用户无法登录。

六、功能测试

        这里我们使用postman进行功能测试,分为以下几种情况:

1、未登录用户访问页面

可以看到,我们自定义的拦截器,成功拦截了未登录用户的访问。

2、用户执行登录,返回token

可以看到,用户成功登录,且后端控制台并未打印信息,说明excludePathPatterns中我们放行了/login起到了作用。

3、普通用户20210919访问只需普通角色就可以访问的功能

可以看到,普通用户成功访问了getInfo()方法,并在访问后,我们的日志记录功能,成功记录了此用户的访问(注意我们的数据库并没有记录访问用户的ID,这个可以自行加,不难)。

4、普通用户20210919访问需admin管理员角色才能访问的方法,例如getRecord()

可以看到,此用户是无法访问需要admin角色的功能的,非法访问已经被成功拦截。

5、管理员用户20210920访问需 admin管理员角色才能访问的方法

首先我执行管理员用户的登录方法,获取管理员token(把login()方法中的return换成20210920即可)。

可以看到拥有admin身份的 20210920用户,成功访问到了指定的日志内容。

6、token失效或被篡改之后的拦截效果

我们随便删除token中的几个字符,模拟token失效或者被恶意篡改

可以看到失效的token是无法正常访问业务的。

以上,我们就完成了使用拦截器实现身份校验、权限控制和日志记录的全部功能。

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

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

相关文章

ASPICE标准快速掌握「3.1. 实践示例」

实践示例 本章内容是最重要的,建议慢下来跟着博主的思路一步一步前进 1. 示例背景说明 假设我们现在是一个Tier1的车窗控制软件开发商,我们给OEM提供软件解决方案 1.1. 本过程目标 根据客户、上级部门、安全团队与质量团队等提出的要求,本项目要求SYS.1过程达到ASPICE过…

【源码】C/C++学生信息管理系统 1024程序员节日快乐

文章目录 题目介绍源码效果展示报告内容 更多源码&#xff1a; 点我跳转目录 题目介绍 1024程序员节日快乐! 使用语言&#xff1a; 此源码包含两个版本: 版本1&#xff1a;C语言 版本2&#xff1a; C 代码量&#xff1a; 650 题目&#xff1a; 学生信息管理系统&#xff0c; …

JAVA-编程基础-11-04-java IO 字符流

Lison <dreamlison163.com>, v1.0.0, 2023.05.07 JAVA-编程基础-11-04-java IO 字符流 文章目录 JAVA-编程基础-11-04-java IO 字符流字符流Reader 和 Writer字符输入流&#xff08;Reader&#xff09;**FileReader构造方法****FileReader读取字符数据** 字符输出流&am…

解决样本不均衡问题

一、样本不均衡问题 样本&#xff08;类别&#xff09;不平衡指的是分类任务中不同类别的训练样例数目差别很大的情况&#xff0c;一般地&#xff0c;样本类别比例&#xff08;多数类VS少数类&#xff09;明显大于1&#xff1a;1&#xff08;例如4&#xff1a;1&#xff09;就…

软考信息安全工程师备考

软考信息安全工程师备考 报名 公告地址 通知是否有考试和考试安排的公告 https://www.ruankao.org.cn/arrange报名地址 https://bm.ruankao.org.cn/sign/welcome刷题 51cto刷题小程序 5cto题库点击进去选择信息安全工程师 可以刷选择题和大题 还可以刷真题非常好用 …

Mysql数据库指定某数据库或某表赋予增删改查操作权限各类划分权限的方法总结实战

一、mysql创建用户只赋予指定数据库的增删改查操作权限 在日常生产运维工作中&#xff0c;我们经常需要给其他厂商或者合作伙伴提供数据库的账号&#xff0c;并且需要指定某个用户只能查询指定的数据库&#xff0c;并且赋予增删改查的指定权限。 &#xff08;1&#xff09;创…

面试算法38:每日温度

题目 输入一个数组&#xff0c;它的每个数字是某天的温度。请计算每天需要等几天才会出现更高的温度。例如&#xff0c;如果输入数组[35&#xff0c;31&#xff0c;33&#xff0c;36&#xff0c;34]&#xff0c;那么输出为[3&#xff0c;1&#xff0c;1&#xff0c;0&#xff…

软硬件架构分层总结

一、前言 软件系统很多架构图我们经常看到是这样的三段 就是这三段就可以演化出很多层 二、硬件架构分层 硬件层&#xff0c;基本是计算机硬件的体系结构&#xff0c;包括硬盘设备&#xff0c;cpu&#xff0c;内存&#xff0c;控制器&#xff0c;运算器&#xff0c;寄存器&am…

清除excel中换行符方法

1、选择要删除或替换换行符的单元格。 2、按 Ctrl H 以打开“查找和替换”对话框。 3、在“查找内容”栏中输入Ctrl J 或 Ctrl Enter 这时会出现一个闪烁的小点。如下图所示&#xff0c;然后点击全部替换即可。

【机器学习合集】参数初始化合集 ->(个人学习记录笔记)

文章目录 综述1. 全零与随机初始化2. 标准初始化(固定方差)3. Xavier初始化(方差缩放)4. He初始化5. 正交初始化6. MSRA初始化 综述 这些是不同的权重初始化方法&#xff0c;用于初始化神经网络的权重参数。它们的主要区别在于初始化权重的策略和数学原理。以下是这些初始化方法…

RTI-DDS代码分析使用介绍

DDS(Data Distribution Service数据分发服务)是对象管理组织OMG的有关分布式实时系统中数据发布的规范。 DDS规范采用了发布/订阅体系结构&#xff0c;但对实时性要求提供更好的支持。DDS是以数据为中心的发布/订阅通信模型。 以下工程基于rti_connext_dds-7.2.0 hello_world.…

Spark简单回顾

星光下的赶路人star的个人主页 大鹏一日同风起&#xff0c;扶摇直上九万里 文章目录 1、Spark1.1 Spark入门1.1.1 Spark部署模式1.1.2 常用端口 1.2 SparkCore1.2.1 RDD不可变和五大属性1.2.2 RDD的弹性1.2.3 cache和Checkpoint的区别1.2.4 算子 1.3 SparkSQL1.4 内核1.4.1提交…

在Linux上安装RStudio工具并实现本地远程访问【内网穿透】

文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 前言 RStudio Server 使你能够在 Linux 服务器上运行你所熟悉和喜爱的 RStudio IDE&#xff0c;并通过 Web 浏览器进行访问…

音频怎么录制?让你轻松成为录音专家!

“音频可以录制吗&#xff1f;如果可以那应该怎么去操作呢&#xff1f;参加了一个配音比赛&#xff0c;需要录制自己配音的视频&#xff0c;但是我不懂怎么录制音频&#xff0c;眼看比赛就要截止了&#xff0c;真的很着急&#xff0c;大家帮帮我。” 音频录制是一项常见但强大…

【数据结构初阶】算法的时间复杂度和空间复杂度

算法的时间复杂度和空间复杂度 1.算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例 3.空间复杂度4. 常见复杂度对比 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的好坏呢&am…

【pdf密码】为什么我的PDF文件不能复制文字?

大家现在接触PDF文件越来越多&#xff0c;有的时候在网上下载的PDF文件打开之后&#xff0c;发现选中文字之后无法复制。甚至其他功能也都无法使用&#xff0c;这是怎么回事&#xff1f;该怎么办&#xff1f; 当我们发现文件打开之后&#xff0c;编辑功能无法使用&#xff0c;很…

设置中添加UI设置系统默认NTP服务器

经常遇到客户在内网中使用无法与ntp服务器通讯导致系统时间错乱&#xff0c;他们想自己替换ntp地址要么是用adb命令要么是重新刷机&#xff0c;这样比较浪费客户的时间。 看了一下Android系统中选择ntp地址的逻辑&#xff0c;发现在framework中已经有了个ntp地址那么系统将会选…

window10彻底关闭系统管理员控制(所有软件以管理员身份运行)

window10彻底关闭系统管理员控制&#xff08;所有软件以管理员身份运行&#xff09; gpedit.msc》计算机配置》windows设置》安全设置》安全选项》 1.用户账户控制&#xff1a;以管理员批准模式运行所有管理员 2.用户账户控制&#xff1a;用于内置管理员账户的管理员批准模式 1…

GeoHash分享

写在前边 复制的一个内部分享&#xff0c;所以可能更偏向PPT性质&#xff0c;本文提出的问题&#xff0c;在末尾参考材料中都会有所提及&#xff0c;包括更深层次的实现原理和各大API对于GeoHash的优化。感兴趣的读者可以拓展看一下。 START GeoHash是一种地址编码&#xff…

又被罚了~新生支付

近日&#xff0c;中国人民银行海南省分行公布行政处罚公示信息内容&#xff0c;具有清算机构新生支付有限公司因存违规行为领罚款单。 行政处罚决定书批准文号“琼银罚决字〔2023〕22号”表明&#xff0c;新生支付有限公司&#xff08;通称“新生支付”&#xff09;存有三项违…