Spring Security入门教程:实现自定义用户配置

news2024/9/20 10:54:05

在上一篇文章中:Spring Security入门教程:利用Spring Security实现安全控制
我们学会了使用Spring Security实现安全控制,学会了他的基础使用,这节课我们来学习一下它的自定义的功能,更深入的了解和使用Spring Security。

登录成功失败和注销

在Spring Security中我们可以自己实现登录成功跟登录失败之后我们系统做的事情。首先我们定义一个登陆失败的类。我们需要实现一个AuthenticationFailureHandler 接口,并且重写他的onAuthenticationFailure方法。

package com.masiyi.springsecuritydemo.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("msg", "登录失败: "+exception.getMessage());
        result.put("status", 500);
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
    }
}

例如在我们这个上面,我们给要用方返回一个登录失败的提示信息。
之后我们在自定义一个登录成功的处理类。他跟上面的登陆失败的。写法类似,也是实现一个AuthenticationSuccessHandler 接口,在这个方法里面。我们给调用端返回一个登录成功的信息。

package com.masiyi.springsecuritydemo.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("msg", "登录成功");
        result.put("status", 200);
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
    }
}

当然类似的还有退出成功的处理类

package com.masiyi.springsecuritydemo.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("msg", "注销成功");
        result.put("status", 200);
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
    }
}

订完这三个自定义的类之后,我们需要给它配置。这样的话,spring security就知道我们需要他做一些什么事情。我们写一个配置类如下。我们刚刚写好的自定义的处理类给他set值进去。这样的话我们再进行一个登录成功,登录失败跟退出操作。他就会执行我们自定义类里面的逻辑。

package com.masiyi.springsecuritydemo.config;

import com.masiyi.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.masiyi.springsecuritydemo.handler.MyAuthenticationSuccessHandler;
import com.masiyi.springsecuritydemo.handler.MyLogoutSuccessHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .mvcMatchers("/user").permitAll()
                .anyRequest().authenticated()
                .and().formLogin()
                .successHandler(new MyAuthenticationSuccessHandler())
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
                .logout().logoutSuccessHandler(new MyLogoutSuccessHandler())
        ;
    }
}

获取登录成功的用户信息


@RestController
public class BasicController {

  
    @RequestMapping("/auth")
    public String hello() {
        //org.springframework.security.web.context.HttpRequestResponseHolder 关键方法
        Authentication authentication = SecurityContextHolder
                .getContext().getAuthentication();
        org.springframework.security.core.userdetails.User principal = (org.springframework.security.core.userdetails.User) authentication.getPrincipal();
        System.out.println("身份 :"+principal.getUsername());
        System.out.println("凭证 :"+authentication.getCredentials());
        System.out.println("权限 :"+authentication.getAuthorities());
        return "hello security";
    }

}

SecurityContextHolder.getContext() 获取Spring Security的上下文,上下文中存储着登录的用户信息Authentication ,利用SecurityContextHolder.getContext().getAuthentication()即可获取

自定义内存用户

我们在WebSecurityConfigurer里面添加如下代码,之前我们都是在yml文件中配置用户和密码,这样的话我们可以在代码中使用内存中的用户信息

 @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager
                = new InMemoryUserDetailsManager();
        UserDetails u1 = User.withUsername("masiyi")
                .password("{noop}111").roles("USER").build();
        inMemoryUserDetailsManager.createUser(u1);
        return inMemoryUserDetailsManager;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

自定义数据源用户

在实际工作和项目中我们肯定不会和上门一样把用户和密码写死,大部分我们是把用户信息存储在数据库中的,所以我们就需要学习在Spring Security中自定义数据源用户,这里以mysql数据库为例

首先在pom文件中引入以下jar包:

      <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.7</version>
        </dependency>

接着在配置文件中加入你的数据库连接信息:

# datasource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity?characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

# mybatis
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.masiyi.springsecuritydemo.entity

在数据库中执行以下sql以创建对应的数据表

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50738
 Source Host           : localhost:3306
 Source Schema         : springsecurity

 Target Server Type    : MySQL
 Target Server Version : 50738
 File Encoding         : 65001

 Date: 20/12/2023 19:47:32
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `name_zh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_product', '商品管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户管理员');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT NULL,
  `accountNonExpired` tinyint(1) DEFAULT NULL,
  `accountNonLocked` tinyint(1) DEFAULT NULL,
  `credentialsNonExpired` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (2, 'admin', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (3, 'blr', '{noop}123', 1, 1, 1, 1);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `rid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `uid`(`uid`) USING BTREE,
  INDEX `rid`(`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);

SET FOREIGN_KEY_CHECKS = 1;

根据user表新建一个对应的实体类,但是这个类要实现UserDetails 接口

package com.masiyi.springsecuritydemo.entity;

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;

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 = new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        roles.forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())));
        return grantedAuthorities;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public String getPassword() {
        return password;
    }


    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    public void setUsername(String username) {
        this.username = username;
    }


    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public Boolean getAccountNonExpired() {
        return accountNonExpired;
    }

    public void setAccountNonExpired(Boolean accountNonExpired) {
        this.accountNonExpired = accountNonExpired;
    }

    public Boolean getAccountNonLocked() {
        return accountNonLocked;
    }

    public void setAccountNonLocked(Boolean accountNonLocked) {
        this.accountNonLocked = accountNonLocked;
    }

    public Boolean getCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
        this.credentialsNonExpired = credentialsNonExpired;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

Role 角色类

package com.masiyi.springsecuritydemo.entity;

public class Role {
    private Integer id;
    private String name;
    private String nameZh;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameZh() {
        return nameZh;
    }

    public void setNameZh(String nameZh) {
        this.nameZh = nameZh;
    }
}

新建一个xml文件对应user

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.masiyi.springsecuritydemo.dao.UserDao">
    <!--查询单个-->
    <select id="loadUserByUsername" resultType="User">
        select id,
               username,
               password,
               enabled,
               accountNonExpired,
               accountNonLocked,
               credentialsNonExpired
        from user
        where username = #{username}
    </select>

    <!--查询指定行数据-->
    <select id="getRolesByUid" resultType="Role">
        select r.id,
               r.name,
               r.name_zh nameZh
        from role r,
             user_role ur
        where r.id = ur.rid
          and ur.uid = #{uid}
    </select>
</mapper>

UserDao 接口

package com.masiyi.springsecuritydemo.dao;

import com.masiyi.springsecuritydemo.entity.Role;
import com.masiyi.springsecuritydemo.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);
}

MyUserDetailService类,实现org.springframework.security.core.userdetails.UserDetailsService接口,重写loadUserByUsername方法。

package com.masiyi.springsecuritydemo.service;

import com.masiyi.springsecuritydemo.dao.UserDao;
import com.masiyi.springsecuritydemo.entity.User;
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.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;
    }
}

在WebSecurityConfigurer类中把

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager
                = new InMemoryUserDetailsManager();
        UserDetails u1 = User.withUsername("masiyi")
                .password("{noop}111").roles("USER").build();
        inMemoryUserDetailsManager.createUser(u1);
        return inMemoryUserDetailsManager;
    }

注释掉,用下面的配置

    @Autowired
    private MyUserDetailService myUserDetailService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService);
    }

这样,就可以读取自定义的用户了,即自定义数据源用户。

注册,加密密码,登录解密密码

在上面我们是直接初始化用户的,我们并没有添加用户的功能,但是在实际项目当中,肯定要有注册的功能,所以我们给前端写一个注册的方法

package com.masiyi.springsecuritydemo.controller;

import com.masiyi.springsecuritydemo.service.MyUserDetailService;
import com.masiyi.springsecuritydemo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author masiyi
 * @Date 2023/12/20
 * @PackageName:com.masiyi.springsecuritydemo.controller
 * @ClassName: UserController
 * @Description: TODO
 * @Version 1.0
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private MyUserDetailService myUserDetailService;


    @PostMapping("/register")
    public Boolean register(@RequestBody User userInfo){
        return myUserDetailService.insertUser(userInfo);
    }

}

MyUserDetailService 类中我们的insertUser方法如下


import com.masiyi.springsecuritydemo.dao.UserDao;
import com.masiyi.springsecuritydemo.entity.User;
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.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

@Component
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private  UserDao userDao;

    @Autowired
    private PasswordEncoder passwordEncoder;



    @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;
    }

    public Boolean insertUser(User userInfo) {
        //登录的时候在 UsernamePasswordAuthenticationFilter里面解密
        userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
        return userDao.insertUser(userInfo);
    }
}

对应的xml文件的sql如下:

    <insert id="insertUser">
        insert into user(username,password) values (#{userInfo.username},#{userInfo.password})
    </insert>

配置类要改的内容如下

在这里插入图片描述

package com.masiyi.springsecuritydemo.config;

import com.masiyi.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.masiyi.springsecuritydemo.handler.MyAuthenticationSuccessHandler;
import com.masiyi.springsecuritydemo.handler.MyLogoutSuccessHandler;
import com.masiyi.springsecuritydemo.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {



    /**
     * 这里有两种方式 authorizeHttpRequests 和 authorizeRequests
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                .mvcMatchers("/user/register").permitAll()
                .anyRequest().authenticated()
                .and().formLogin()
                .successHandler(new MyAuthenticationSuccessHandler())
                .failureHandler(new MyAuthenticationFailureHandler())
                .and()
                .logout().logoutSuccessHandler(new MyLogoutSuccessHandler())
                .and().csrf().disable()
        ;
    }
//
//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager inMemoryUserDetailsManager
//                = new InMemoryUserDetailsManager();
//        UserDetails u1 = User.withUsername("masiyi")
//                .password("{noop}111").roles("USER").build();
//        inMemoryUserDetailsManager.createUser(u1);
//        return inMemoryUserDetailsManager;
//    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new MyUserDetailService();
    }



    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());

    }

    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }
}

至此spring security实现自定义用户配置,我们就讲到这里。下面我会给大家讲一下spring security的授权的功能。

项目的地址就在
https://gitee.com/WangFuGui-Ma/spring-security-admin
如果大家对这篇文章或者专栏有兴趣或者对大家有所帮助的话,欢迎关注点赞。加评论。
我们spring security的进阶专栏见。

在这里插入图片描述

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

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

相关文章

计算机组成结构—寻址方法

目录 一、指令寻址 二、数据寻址 1.立即寻址 2.直接寻址 3.间接寻址 4.隐含寻址 5.寄存器寻址 6.寄存器间接寻址 7.基址寻址 8.变址寻址 9.相对寻址 10. 堆栈寻址 寻址方式是寻找指令或操作数有效地址的方式&#xff0c;也就是指确定本条指令的数据地址&#xff0c;…

C++错题集(持续更新ing)

Day 1 一、选择题 解析&#xff1a; 在数字不会溢出的前提下&#xff0c;对于正数和负数&#xff0c;有&#xff1a; 1&#xff09;左移n位&#xff0c;相当于操作数乘以2的n次方&#xff1b; 2&#xff09;右移n位&#xff0c;相当于操作数除以2的n次方。 解析&#xff1a…

深度盘点在当今经济形势下资深项目经理或PMO的或去或从

在当今经济形势下&#xff0c;资深项目经理&#xff08;Project Manager&#xff09;或项目管理办公室&#xff08;PMO&#xff09;的去向和选择受到多种因素的影响。以下是对他们可能面临的或去或从的深度盘点&#xff1a; 1、发展去向 1. 深化专业领域&#xff1a;在经济形势…

2024年【制冷与空调设备运行操作】考试内容及制冷与空调设备运行操作考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作考试内容是安全生产模拟考试一点通生成的&#xff0c;制冷与空调设备运行操作证模拟考试题库是根据制冷与空调设备运行操作最新版教材汇编出制冷与空调设备运行操作仿真模拟考试。2024年【制冷…

如何实现Linux双网卡同时连接内网和外网的配置?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

快速查看字符对应的ASCII码

1、借助gdb查看 打印字符串用双引号括起来打印单个字符用单引号括起来x 表示十六机制d 表示十进制t 表示二进制 2、借助二进制查看软件 第一步&#xff1a;把要查看的字符保存到文本文件中第二步&#xff1a;借助二进制查看工具&#xff08;比如&#xff1a;Hex Editor Neo&am…

Windows 10无法远程桌面连接:原因及解决方案

在信息技术日益发展的今天&#xff0c;远程桌面连接已成为企业日常运维、技术支持乃至个人用户远程办公的必备工具。然而&#xff0c;有时我们可能会遇到Windows 10无法远程桌面连接的问题&#xff0c;这无疑会给我们的工作和生活带来诸多不便。 原因分析 1、远程访问未启用&a…

对比测评3款BI分析工具

前不久&#xff0c;一位准备入职阿里的学弟问我&#xff0c;他要做电商数据分析&#xff0c;电商有庞杂的标签、模型、数据和业务逻辑&#xff0c;菜鸟应该要具备什么样的分析能力啊&#xff1f; 我看了他的岗位职责&#xff0c;主要是负责经营决策支持、专题分析和数据看板搭…

智能决策引擎架构设计

智能决策引擎概述 智能决策引擎系统是在大数据支撑下,根据行业专家经验制定规则策略、以及机器学习/深度学习/AI领域建立的模型运算,对当前的业务进行全面的评估,并给出决策结果的一套系统。 一套商业决策引擎系统动辄百万而且需要不断加钱定制,大多数企业最终仍会走上自研…

纯血鸿蒙APP实战开发——Worker子线程中解压文件

介绍 本示例介绍在Worker 子线程使用ohos.zlib 提供的zlib.decompressfile接口对沙箱目录中的压缩文件进行解压操作&#xff0c;解压成功后将解压路径返回主线程&#xff0c;获取解压文件列表。 效果图预览 使用说明 点击解压按钮&#xff0c;解压test.zip文件&#xff0c;显…

ASP.NET在线二手交易系统的设计与实现

摘 要 随着当今社会信息技术的进步&#xff0c;基于互联网的各种应用日益受到了人们的重视&#xff0c;二手商品的重新利用也逐渐被人们关注&#xff0c;二手交易系统就在这种形势下产生了&#xff0c;它利用网络&#xff0c;改变了人们的购物方式。 本文是基于现代二手交易…

网络安全护网行动:形式主义还是真有价值?

中国每年都投入大量人力物力进行护网行动&#xff0c;如网络攻防演练、黑客技术研究等。有人质疑这些行动是否只是形式主义&#xff0c;缺乏真正的价值。然而&#xff0c;本文将深入解释这些护网行动的原因&#xff0c;并阐明其对信息安全发展的真实价值。 网络信息安全问题的…

Google Ads被暂停的原因,如何防范?

跨境出海业务少不了需要做Google Ads推广业务&#xff1b;其中让投手们闻风丧胆的消息就是帐户被暂停。当 Google 检测到任何违反其政策且可能损害用户在线体验的行为时&#xff0c;就会发生这种情况。那么如何在做广告推广的同时&#xff0c;保证账号不被封禁呢&#xff1f;看…

学前端网络安全这块还不懂?细说CSRF

什么是CSRF&#xff1f; 举个栗子&#xff0c;比如我们需要在某个博客上删除一个文章&#xff0c;攻击者首先在自己的域构造一个页面&#xff0c;使用了一个img标签&#xff0c;其地址指向了删除博客的链接。攻击者诱使目标用户&#xff0c;也就是博客主访问这个页面&#xff…

Linux系统 的持续学习

昨天学习了目录结构、补充命令和配置网络&#xff0c;其中配置网络用了nat方法&#xff0c;今天学习用桥接方法&#xff0c;通配符、正则表达式的一部分内容。 桥接模式 如果重网卡失败&#xff1a; 1.检查配置文件是否正确 2.检查虚拟器编辑器有没有选对&#xff08;网卡类…

二进制搭建 k8s 单 Master 集群架构

一、单机 matser 部署环境 mater节点mater01192.168.80.7kube-apiserver、kube-controller-manager、kube-scheduler、 etcdnode节点node01192.168.80.11kubelet kube-proxy docker &#xff08;容器引擎&#xff09;node02192.168.80.12kubelet kube-proxy docker &#xff0…

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 删除并获得点数(难度⭐⭐)(76)

1. 题目解析 题目链接&#xff1a;LCR 091. 粉刷房子 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 1. 状态定义 在解决这类问题时&#xff0c;我们首先需要根据题目的具体要求来定义状态。针对房屋粉刷问题&#…

《系统架构设计师教程(第2版)》第4章-信息安全技术基础知识-02-信息加密技术

文章目录 1. 信息加密技术1.1 数据加密1.2 对称密钥加密算法1&#xff09;数据加密标准&#xff08;DES)2&#xff09;三重DES&#xff08;Triple-DES&#xff09;3&#xff09;国际数据加密算法&#xff08;IDEA&#xff09;4&#xff09;高级加密标准&#xff08;AES&#xf…

如何管理测试用例?测试用例有什么管理工具?YesDev

3.1 测试用例 测试用例(Test Case) 是指对一项特定的软件产品进行测试任务的描述&#xff0c;体现测试方案、方法、技术和策略。其内容包括测试目标、测试环境、输入数据、测试步骤、预期结果等。简单地认为&#xff0c;测试用例是为某个特殊目标而编制的一组测试输入、执行条…

STM32 各外设GPIO配置

高级定时器TIM1/TIM8 通用定时器TIM2/3/4/5 USART SPI I2S I2C接口 BxCAN SDIO ADC/DAC 其它I/O功能