SpringBoot security 安全认证(一)——登录验证

news2025/2/3 1:03:36

本节内容:使用springboot自动security模块实现用户登录验证功能;
登录过程如下图:
在这里插入图片描述
AuthenticationManager内容实现用户账号密码验证,还可以对用户状态(启用/禁用),逻辑删除,账号是否被锁定等判断。密码加密方式内置了好几种,我使用的是BCryptPasswordEncoder。那么我们在用户注册时密码要使用 new BCryptPasswordEncoder().encode(pwd)进行加密。

代码实现过程:
1、引入相关依赖;
2、创建UserDetails实现类LoginUser;
3、创建UserDetailsService实现类UserDetailsServiceImpl;
4、SecurityConfiguration配置,将UserDetailsServiceImpl注入相关对象,并配置加密算法;
5、实现账号密码验证;

pom.xml引入依赖

<dependencies>

        <!-- 实现对 Spring MVC 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring security 安全认证 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- Token生成与解析-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        <!-- 解析客户端操作系统、浏览器等 -->
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
        </dependency>
<!-- String工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

        <!-- 阿里JSON解析器 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.34</version>
        </dependency>

        <!-- 方便等会写单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- 引入 Swagger 依赖 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>

        <!-- 引入 Swagger UI 依赖,以实现 API 接口的 UI 界面 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
    </dependencies>

UserDetails的实现类LoginUser

是数据库用户到Spring用户的转换,提供给Spring内部获取用户账号、状态等的。

package com.luo.chengrui.labs.lab04.model;

import com.alibaba.fastjson2.annotation.JSONField;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

/**
 * 登录用户身份权限
 *
 * @author ruoyi
 */
public class LoginUser implements UserDetails {
    private static final long serialVersionUID = 1L;

    /**
     * 用户ID
     */
    private String userId;


    /**
     * 用户唯一标识
     */
    private String token;

    /**
     * 登录时间
     */
    private Long loginTime;

    /**
     * 过期时间
     */
    private Long expireTime;

    /**
     * 登录IP地址
     */
    private String ipaddr;

    /**
     * 登录地点
     */
    private String loginLocation;

    /**
     * 浏览器类型
     */
    private String browser;

    /**
     * 操作系统
     */
    private String os;

	//数据库用户映射表
    private SysUser user;

    public LoginUser(String userId, SysUser user) {
        this.userId = userId;
        this.user = user;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public Long getLoginTime() {
        return loginTime;
    }

    public void setLoginTime(Long loginTime) {
        this.loginTime = loginTime;
    }

    public Long getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(Long expireTime) {
        this.expireTime = expireTime;
    }

    public String getIpaddr() {
        return ipaddr;
    }

    public void setIpaddr(String ipaddr) {
        this.ipaddr = ipaddr;
    }

    public String getLoginLocation() {
        return loginLocation;
    }

    public void setLoginLocation(String loginLocation) {
        this.loginLocation = loginLocation;
    }

    public String getBrowser() {
        return browser;
    }

    public void setBrowser(String browser) {
        this.browser = browser;
    }

    public String getOs() {
        return os;
    }

    public void setOs(String os) {
        this.os = os;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @JSONField(serialize = false)
    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

	//账号未过期 
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

	//账号未被锁定
    @JSONField(serialize = false)
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

	//密码未过期
    @JSONField(serialize = false)
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 是否可用 ,禁用的用户不能身份验证
     *
     * @return
     */
    @JSONField(serialize = false)
    @Override
    public boolean isEnabled() {
        return true;
    }
}

LoginUser类看着字段较多,基本都是和业务相关,比如记录登录用户的IP,地址,登录时间等的。看实际情况,可以优化掉的,仅保留SysUser对象也是完全可以的。

UserDetailsService接口实现类 UserDetailsServiceImpl

实现根据用户名获取用户信息,提供给spring内部调用的。

package com.luo.chengrui.labs.lab04.service;

import com.luo.chengrui.labs.lab04.model.LoginUser;
import com.luo.chengrui.labs.lab04.model.SysUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Service;
import org.springframework.util.StringUtils;

import java.util.Objects;

/**
 * 用户验证处理
 *
 * @author ruoyi
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = userService.selectUserByUserName(username);
        if (user == null) {
            log.info("登录用户:{} 不存在.", username);
            throw new RuntimeException("登录用户:" + username + " 不存在");
        } else if (Objects.equals(1, user.getDelFlag())) {
            log.info("登录用户:{} 已被删除.", username);
            throw new RuntimeException("对不起,您的账号:" + username + " 已被删除");
        } else if (Objects.equals(0, user.getStatus())) {
            log.info("登录用户:{} 已被停用.", username);
            throw new RuntimeException ("对不起,您的账号:" + username + " 已停用");
        }
        return createLoginUser(user);
    }

    public UserDetails createLoginUser(SysUser user) {
        return new LoginUser(user.getUserId(),user);
    }
}

1、以上判断用户状态和是否被删除等操作也可以交由spring去做,你只要在LoginUser类中相关方法中返回结果即可。在这里可以做额外的合法性判断。(如这个账号登录次数超限了,并且可以提示相关登录失败信息)
2、实现了根据用户账号查询用户信息方法,最后返回了我们上面定义的LoginUser对象。

LoginUservice登录实现

package com.luo.chengrui.labs.lab04.service;

import com.luo.chengrui.labs.lab04.model.LoginUser;
import com.luo.chengrui.labs.lab04.utils.UUID;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2024/01/05
 */
@Service
public class LoginService {

    @Resource
    private AuthenticationManager authenticationManager;

    private static final String secret = "abcdefghijklmnopqrstuvwxyz";

    public String login(String username, String password) {
        // 用户验证
        Authentication authentication = null;
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
        	//这里应该按不同异常类型分别捕获,更精确的提示用户登录失败的原因。看业务需要
            throw new RuntimeException("用户名或密码错误");
        } finally {

        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 生成token
        return createToken(loginUser);
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser) {
        String token = UUID.fastUUID().toString();
        loginUser.setToken(token);
        Map<String, Object> claims = new HashMap<>();
        claims.put("LOGIN_USER_KEY", token);
        return createToken(claims);
    }

    private String createToken(Map<String, Object> claims) {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

    /**
     * 刷新令牌有效期,是指刷新缓存中存储的token信息。我们本示例中暂不做缓存。
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + 30 * 60 * 1000);
    }
}


SecurityConfig

1、创建鉴权管理器:AuthenticationManager ;
2、设置请求过滤;
3、设置密码加密算法;
4、设置用户获取对象;

package com.luo.chengrui.labs.lab04.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.filter.CorsFilter;

/**
 * spring security配置
 *
 * @author 
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * anyRequest          |   匹配所有请求路径
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 过滤请求
                .authorizeRequests()
                // 所有请求均以放行
                .anyRequest().permitAll()
                .and()
                .headers().frameOptions().disable();
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 设置密码加密算法
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}

到此,使用Spring security登录验证的核心代码就写完了。再一个实现UserService类实现从数据库查询用户

UserServiceImpl

package com.luo.chengrui.labs.lab04.service;

import com.luo.chengrui.labs.lab04.model.SysUser;
import com.luo.chengrui.labs.lab04.utils.SecurityUtils;
import com.luo.chengrui.labs.lab04.utils.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2024/01/31
 */
@Service
public class UserService {

    private static final Map<String, SysUser> userMap = new HashMap<>();

    static {
        userMap.put("admin", new SysUser("1", "admin", SecurityUtils.encryptPassword("admin123"), 1));
    }

    public List<SysUser> selectUser() {
        return userMap.entrySet().stream().map(item -> item.getValue()).collect(Collectors.toList());
    }

    public SysUser selectUserByUserName(String username) {
        SysUser user = userMap.get(username);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        return user;
    }

    public SysUser registerUser(SysUser user) {
        if (user == null) {
            throw new RuntimeException("用户信息不能为空");
        }
        if (StringUtils.isEmpty(user.getUsername())) {
            throw new RuntimeException("用户名不能为空");
        }
        if (StringUtils.isEmpty(user.getPassword())) {
            throw new RuntimeException("密码不能为空");
        }
        if (userMap.get(user.getUsername()) != null) {
            throw new RuntimeException("用户名已经存在");
        }
        user.setUserId(UUID.randomUUID().toString());
        user.setStatus(1);
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        userMap.put(user.getUserId(), user);
        return user;

    }

    public SysUser selectUser(String username) {
        return userMap.get(username);
    }
}

简单模拟了根据账号查询用户的接口,实际是事先在userMap中放了一个admin用户而已。

SysUser数据库对象

package com.luo.chengrui.labs.lab04.model;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2024/01/31
 */
public class SysUser {

    private String userId;

    private String username;

    private String password;

    private Integer delFlag;

    private Integer status;

    public SysUser() {
    }

    public SysUser(String userId, String username, String password,  Integer status) {
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.status = status;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public Integer getDelFlag() {
        return delFlag;
    }

    public void setDelFlag(Integer delFlag) {
        this.delFlag = delFlag;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }
}

controller

package com.luo.chengrui.labs.lab04.controller;

import com.luo.chengrui.labs.lab04.model.AjaxResult;
import com.luo.chengrui.labs.lab04.model.LoginBody;
import com.luo.chengrui.labs.lab04.service.LoginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/17
 */
@RestController
@Api(tags = "用户 API 接口")
public class LoginController {

    @Autowired
    LoginService loginService;

    @ApiOperation(value = "用户登录 ", notes = "目前仅仅是作为测试,所以返回用户全列表")
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody) {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword());
        ajax.put("token", token);
        return ajax;
    }

}

swagger配置

package com.luo.chengrui.labs.lab04.config;

import com.luo.chengrui.labs.lab04.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

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

/**
 * 访问地址:/swagger-ui.html
 *
 * @author
 * @version 1.0.0
 * @description
 * @createTime 2023/07/17
 */
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Autowired
    TokenService tokenService;

    @Bean
    public Docket createRestApi() {
		/* 让swagger页面上的每个接口都添加一个Header参数,用来传递token参数*/
        ParameterBuilder ticketPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<Parameter>();
        ticketPar.name(tokenService.getHeader()).description("user ticket")//Token 以及Authorization 为自定义的参数,session保存的名字是哪个就可以写成那个
                .modelRef(new ModelRef("string")).parameterType("header")
                .required(false).build(); //header中的ticket参数非必填,传空也可以
        pars.add(ticketPar.build()); //根据每个方法名也知道当前方法在设置什么参数
       

		 // 创建 Docket 对象
        return new Docket(DocumentationType.SWAGGER_2) // 文档类型,使用 Swagger2
                .apiInfo(this.apiInfo()) // 设置 API 信息
                // 扫描 Controller 包路径,获得 API 接口
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.luo.chengrui.labs.lab04.controller"))
                .paths(PathSelectors.any())
                // 构建出 Docket 对象
                .build()
                .globalOperationParameters(pars);
    }

    /**
     * 创建 API 信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("测试接口文档示例")
                .description("我是一段描述")
                .version("1.0.0") // 版本号
                .contact(new Contact("XX", "http://localhost", "luodz@gmail.com")) // 联系人
                .build();
    }
}

Swaager请求示例:
在这里插入图片描述
响应结果:
在这里插入图片描述

后台方法调用:
1、调用 UserDetailsServiceImpl.LoadUserByUsername方法,获取用户信息;
在这里插入图片描述

2、判断用户各种可用状态和密码合法性。
在这里插入图片描述
小结:本节主要演示了如何使用Spring去实现登录验证
1、创建UserDetails接口实现类,UserDetails是Spring内部定义的登录用户信息,包含账号、密码、删除状态、禁用状态、锁定状态、密码过期状态;
2、创建UserDetailsService接口实现类,实现loadUserByUsername(String username)方法;
3、用户合法性验证,仅一行代码即可完成用户合法性验证:

Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();

下一节,咱们可以实现对接口访问的拦截了。

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

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

相关文章

RS485通信在钡铼技术的LoRa网关中的应用

在钡铼技术的LoRa网关中&#xff0c;RS485通信协议的应用是一个典型的示例&#xff0c;展现了如何将传统的工业通信技术与现代的物联网(IoT)解决方案相结合&#xff0c;以实现更加高效、可靠的数据传输和设备管理。RS485作为一种广泛使用的串行通信协议&#xff0c;因其稳定性高…

分类预测 | Matlab实现SCN-Adaboost随机配置网络模型SCN的Adaboost数据分类预测/故障识别

分类预测 | Matlab实现SCN-Adaboost随机配置网络模型SCN的Adaboost数据分类预测/故障识别 目录 分类预测 | Matlab实现SCN-Adaboost随机配置网络模型SCN的Adaboost数据分类预测/故障识别分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCN-Adaboost随机配置网…

2024最新!软件测试高频面试题基础知识点分享

近期也算是抽取出大部分休息的时间&#xff0c;为大家准备了一份通往大厂面试的小捷径&#xff0c;准备了一整套软件测试复习面试的刷题以及答案&#xff0c;我知道很多同学不知道怎么复习&#xff0c;不知道学习过程中哪些才是重点&#xff0c;其实&#xff0c;你们经历过的事…

C/C++ 回调函数 callback 异步编程

一、C语言的回调函数 1.小试牛刀 #include <iostream> using namespace std; #include <memory> #include <stdlib.h>int add(int a, int b) {return a b; }void test01() {// 函数指针可以指向任何类型的函数&#xff0c;只要函数的参数列表和返回值类型…

数据结构 归并排序详解

1.基本思想 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。 将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序…

HAL库配置片内FLASH读写

一、FLASH简介 不同型号的 STM32F40xx/41xx&#xff0c;其 FLASH 容量也有所不同&#xff0c;最小的只有 128K 字节&#xff0c;最大 的则达到了 1024K 字节。我们的探索者开发板选择的是 STM32F407ZGT6 的 FLASH 容量为 1024K 字节。 主存储器&#xff0c;存放代码和数据常数&…

关于ZYZ旋转和XYZ旋转

ZYZ旋转和XYZ旋转 概述1、XYZ旋转2、ZYZ旋转 概述 以下公式默认为右手坐标系&#xff1b;ZYZ通常可以避免死解情况&#xff0c;因此在六轴末端解算时常被用到&#xff1b;参考文章 1、XYZ旋转 XYZ旋转一般是绕固定轴旋转(外旋)&#xff0c;旋转矩阵的构成为&#xff1a;RzRy…

关于如何利用ChatGPT提高编程效率的

自从去年ChatGPT3.5推出以后&#xff0c;这一年时间在编程过程中我也在慢慢熟悉人工智能的使用&#xff0c;目前来看即使是免费的ChatGPT3.5对于编程效率的提升也是有很大帮助的&#xff0c;虽然在使用过程中确实出现了一些问题&#xff0c;本文记录下我的一些心得体会和用法。…

ubuntu安装docker及插件docker-compose(详细图文)

目录 一、摘要 二、说明 三、安装docker及compose 方式一&#xff1a;采用apt存储库安装 1.设置 Docker 的存储库 2.1安装最新Docker包及其常用插件docker-compose 2.2安装指定 Docker 包及其常用插件docker-compose &#xff08;1&#xff09;列出所有存储库中docker版…

Docker中配置MySql环境

目录 一、简单安装 1. 首先从Docker Hub中拉取镜像 2. 启动尝试创建MySQL容器&#xff0c;并设置挂载卷。 3. 查看mysql8这个容器是否启动成功 4. 如果已经成功启动&#xff0c;进入容器中简单测试 4.1 进入容器 4.2 登录mysql中 4.3 进行简单添加查找测试 二、主从复…

Linux实验记录:使用Apache的虚拟主机功能

前言&#xff1a; 本文是一篇关于Linux系统初学者的实验记录。 参考书籍&#xff1a;《Linux就该这么学》 实验环境&#xff1a; VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 正文&#xff1a; 目录 前言&#xff1a; 正文&…

行业报告 | 工业机器人发展研报

原创 | 文 BFT机器人 工业机器人是一种能够自动执行任务的机器装置&#xff0c;是靠自身动力和控制能力来实现各种功能的一种机器。在工业生产过程中&#xff0c;可以大幅提高生产效率和产品质量。 随着科技的不断进步&#xff0c;工业机器人已经成为现代制造业的重要组成部分…

【Unity3D小技巧】Unity3D中UI控制解决方案

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中总是会控制UI界面&#xff0c;如何优雅的控制UI界面是…

亚马逊精品广告推广怎么做?亚马逊怎么看精品广告推广?站斧浏览器

亚马逊精品广告推广怎么做? 对于想要在亚马逊平台上进行精品广告推广的商家来说&#xff0c;以下是一些常见的操作步骤和注意事项。 首先&#xff0c;商家需要在亚马逊广告平台注册并创建广告账户。亚马逊广告平台提供了专门的自助式广告管理工具&#xff0c;商家可以通过该…

数据结构-图的最小生成树

最小生成树介绍 最小生成树(Minimum Cost Spanning Tree)是代价最小的连通网的生成树&#xff0c;即该生成树上的边的权值和最小 最小生成树的性质&#xff1a; 必须使用且仅使用连通网中的n-1条边来联结网络中的n个顶点&#xff1b; 不能使用产生回路的边&#xff1b; 各…

linux vim 异常退出 异常处理 交换文件

交换文件 *.swp 格式 同时是隐藏的 如在vim一个文件&#xff0c; 在没有正常退出&#xff0c; 如直接断开连接 在次编辑这个文件 会出现下图的错误 解决方案&#xff1a; 直接删除这个交换文件即可 rm -fr .zen.txt.swp

css3 属性 backface-visibility 的实践应用

backface-visibility 是一个用于控制元素在面对屏幕不同方向时的可见性的CSS3特性。它有两个可能的值&#xff1a; visible&#xff1a;当元素不面向屏幕&#xff08;即背面朝向用户&#xff09;时&#xff0c;元素的内容是可以被看到的。hidden&#xff1a;当元素不面向屏幕…

【计算机网络】Socket的SO_REUSEADDR选项与TIME_WAIT

SO_REUSEADDR用于设置套接字的地址重用。当一个套接字关闭后&#xff0c;它的端口可能会在一段时间内处于TIME_WAIT状态&#xff0c;此时无法立即再次绑定相同的地址和端口。使用SO_REUSEADDR选项可以允许新的套接字立即绑定到相同的地址和端口&#xff0c;即使之前的套接字仍处…

如何使用Linux Archcraft结合内网穿透实现SSH远程连接

&#x1f4d1;前言 本文主要是使用Linux Archcraft结合内网穿透实现SSH远程连接的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#…

过年了,程序员们,请多关照自己!休息是为了走得更远!

文章目录 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff01;一、理解“卷”背后的代价二、休息是为了走得更远三、关注健康&#xff0c;远离“过劳”四、平衡工作与生活&#xff0c;追求全面发展 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff0…