springboot权限验证学习-上

news2024/11/16 3:39:42

创建maven项目

创建父工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这类项目和原来项目的区别在于,打包方式是pom
由于pom项目一般都是用来做父项目的,所以该项目的src文件夹可以删除掉。

创建子工程

在这里插入图片描述
子工程pom.xml
在这里插入图片描述
父工程pom.xml
在这里插入图片描述

添加依赖

父工程导入依赖包

<!--导入springboot 父工程-->
<parent>
  <artifactId>spring-boot-starter-parent</artifactId>
  <groupId>org.springframework.boot</groupId>
  <version>2.5.3</version>
</parent>

子工程添加依赖包

<dependencies>
  <!--web依赖包, web应用必备-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!--MySQL,连接MySQL必备-->
      <dependency>
          <groupId>com.mysql</groupId>
          <artifactId>mysql-connector-j</artifactId>
          <version>8.0.33</version>
      </dependency>
      <!--MyBatis-plus,ORM框架,访问并操作数据库-->
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.4.0</version>
      </dependency>
     <!-- lombok -->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <scope>provided</scope>
      </dependency>
</dependencies>

stu01下main下新建resources文件夹,该文件夹下新建application.yml

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/javaauth01?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml                #对应mapper映射xml文件所在路径
  type-aliases-package: com.wujialiang.auth.entity          #对应实体类路径

resources下新建mapper

修改app.java

package com.wujialiang.auth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 网站入口
 *
 */
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        // 第一个参数是该类的名字.class 第二个参数是main方法中的参数
        SpringApplication.run(App.class, args);
    }
}

jwt登录验证

实现登录

entity下新建User实体

package com.wujialiang.auth.entity;


import lombok.Data;

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

新建SQL

create table User(
    Id int primary key AUTO_INCREMENT,
    UserName varchar(50),
    Password varchar(50)
);

insert into User (username, password) values('admin','admin');
insert into User (username, password) values('wjl','xiaoming');

在这里插入图片描述

新建mapper文件夹,新建UserMapper接口

package com.wujialiang.auth.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wujialiang.auth.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

//@MapperScan("com.example.playspring.mapper")
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * 用户登录判断
     * @param userName
     * @param password
     * @return
     */
    boolean userLogin(@Param("userName") String userName,@Param("password") String password);
}


resource下mapper下新建UserMapper.xml

<?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.wujialiang.auth.mapper.UserMapper">
    <select id="userLogin" resultType="java.lang.Boolean">
        select count(id) from User where username=#{userName} and Password=#{password}
    </select>
</mapper>

新建UserController

package com.wujialiang.auth.controller;

import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.mapper.UserMapper;
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.RestController;

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        boolean isLogin = userMapper.userLogin(user.getUsername(), user.getPassword());
        if (isLogin) {
            return "登录成功";
        }
        return "账号密码错误";
    }
}

访问接口调试http://localhost:8080/login
在这里插入图片描述

接入jwt

添加maven包

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

新建JwtUtil.java

package com.wujialiang.auth.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.Date;

/**
 * jwt帮助类
 */
public class JwtUtil {
    /**
     * 这个秘钥是防止JWT被篡改的关键,随便写什么都好,但决不能泄露
     */
    private final static String secretKey = "1234567890";
    /**
     * 过期时间目前设置成2天,这个配置随业务需求而定
     */
    private final static Duration expiration = Duration.ofHours(2);

    /**
     * 生成JWT
     *
     * @param userName 用户名
     * @return JWT
     */
    public static String generate(String userName) {
        // 过期时间
        Date expiryDate = new Date(System.currentTimeMillis() + expiration.toMillis());

        return Jwts.builder()
                .setSubject(userName) // 将userName放进JWT
                .setIssuedAt(new Date()) // 设置JWT签发时间
                .setExpiration(expiryDate)  // 设置过期时间
                .signWith(SignatureAlgorithm.HS512, secretKey) // 设置加密算法和秘钥
                .compact();
    }

    /**
     * 解析JWT
     *
     * @param token JWT字符串
     * @return 解析成功返回Claims对象,解析失败返回null
     */
    public static Claims parse(String token) {
        // 如果是空字符串直接返回null
        if (StringUtils.isEmpty(token)) {
            return null;
        }

        // 这个Claims对象包含了许多属性,比如签发时间、过期时间以及存放的数据等
        Claims claims = null;
        // 解析失败了会抛出异常,所以我们要捕捉一下。token过期、token非法都会导致解析失败
        try {
            claims = Jwts.parser()
                    .setSigningKey(secretKey) // 设置秘钥
                    .parseClaimsJws(token)
                    .getBody();
        } catch (JwtException e) {
            // 这里应该用日志输出,为了演示方便就直接打印了
            System.err.println("解析失败!");
        }
        return claims;
    }
}

修改登录接口

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        boolean isLogin = userMapper.userLogin(user.getUsername(), user.getPassword());
        if (isLogin) {
            // 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)
            return JwtUtil.generate(user.getUsername());
        }
        return "账号密码错误";
    }
}

测试登录,返回token,后面要用
在这里插入图片描述

编写需要登陆后才能获取的接口

@GetMapping("/jwttest")
public String api(HttpServletRequest request) {
    // 从请求头中获取token字符串
    String jwt = request.getHeader("Authorization");
    // 解析失败就提示用户登录
    if (JwtUtil.parse(jwt) == null) {
        return "请先登录";
    }
    // 解析成功就执行业务逻辑返回数据
    return "api成功返回数据";
}

没有token
在这里插入图片描述
请求头中添加token
在这里插入图片描述

拦截器统一处理用户是否登录

新建LoginInterceptor

package com.wujialiang.auth.interceptor;

import com.wujialiang.auth.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

/**
 * 登录拦截器
 */
public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 简单的白名单,登录这个接口直接放行
        if ("/login".equals(request.getRequestURI())) {
            return true;
        }

        // 从请求头中获取token字符串并解析
        Claims claims = JwtUtil.parse(request.getHeader("Authorization"));
        // 已登录就直接放行
        if (claims != null) {
            return true;
        }

        // 走到这里就代表是其他接口,且没有登录
        // 设置响应数据类型为json(前后端分离)
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        // 设置响应内容,结束请求
        out.write("请先登录");
        out.flush();
        out.close();
        return false;
    }
}

拦截器类写好之后,别忘了要使其生效,这里我们直接让SpringBoot启动类实现WevMvcConfigurer接口来做:

package com.wujialiang.auth;

import com.wujialiang.auth.interceptor.LoginInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 网站入口
 *
 */
@SpringBootApplication
public class App implements WebMvcConfigurer {
    public static void main(String[] args)  {
        // 第一个参数是该类的名字.class 第二个参数是main方法中的参数
        SpringApplication.run(App.class, args);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 使拦截器生效
        registry.addInterceptor(new LoginInterceptor());
    }
}

修改jwttest接口

@GetMapping("/jwttest")
public String api() {
   return "api成功返回数据";
}

这样接口就简洁多了
在这里插入图片描述
在这里插入图片描述

上下文对象

为了方便在其他地方获取用户信息,特别是service层
首先我们定义一个上下文类,这个类专门存储JWT解析出来的用户信息。我们要用到ThreadLocal,以防止线程冲突

ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

package com.wujialiang.auth.context;

public final class UserContext {
    private static final ThreadLocal<String> user = new ThreadLocal<String>();

    public static void add(String userName) {
        user.set(userName);
    }

    public static void remove() {
        user.remove();
    }

    /**
     * @return 当前登录用户的用户名
     */
    public static String getCurrentUserName() {
        return user.get();
    }
}

这个类创建好之后我们还需要在拦截器里做下处理

// 从请求头中获取token字符串并解析
Claims claims = JwtUtil.parse(request.getHeader("Authorization"));
// 已登录就直接放行
if (claims != null) {
    // 将我们之前放到token中的userName给存到上下文对象中
    UserContext.add(claims.getSubject());
    return true;
}

在这里插入图片描述
新建UserService

package com.wujialiang.auth.service;

import com.wujialiang.auth.context.UserContext;
import com.wujialiang.auth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public Boolean userLogin(String userName,String password){
        boolean isLogin = userMapper.userLogin(userName, password);
        return isLogin;
    }

    public void doSomething() {
        String currentUserName = UserContext.getCurrentUserName();
        System.out.println("Service层---当前用户登录名:" + currentUserName);
    }
}

修改UserController

package com.wujialiang.auth.controller;

import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.service.UserService;
import com.wujialiang.auth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        boolean isLogin = userService.userLogin(user.getUsername(), user.getPassword());
        if (isLogin) {
            // 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)
            return JwtUtil.generate(user.getUsername());
        }
        return "账号密码错误";
    }

    @GetMapping("/jwttest")
    public String api() {
        userService.doSomething();
        return "api成功返回数据";
    }
}

在这里插入图片描述
至此jwt登录验证完成

权限控制

页面权限

页面权限非常容易理解,就是有这个权限的用户才能访问这个页面,没这个权限的用户就无法访问,它是以整个页面为维度,对权限的控制并没有那么细,所以是一种粗颗粒权限。
最直观的一个例子就是,有权限的用户就会显示所有菜单,无权限的用户就只会显示部分菜单:
资源表设计

create table resource(
    id int primary key AUTO_INCREMENT comment '主键',
    userId int comment '用户id',
    path varchar(255) comment 'URL路径'
);

insert into resource (userId, path) value(1,'/');
insert into resource (userId, path) value(1,'/user/account');
insert into resource (userId, path) value(1,'/user/role');
insert into resource (userId, path) value(1,'/data');
insert into resource (userId, path) value(2,'/user/role');
insert into resource (userId, path) value(2,'/data');

用户1有全部页面的权限,用户2有部分页面的权限
新建Resource

package com.wujialiang.auth.entity;

import lombok.Data;

@Data
public class Resource {
    private int id;
    private int userId;
    private String path;
}

新建ResouceMapper

package com.wujialiang.auth.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wujialiang.auth.entity.Resource;

import java.util.List;

@Repository
@Mapper
public interface ResouceMapper extends BaseMapper<Resource> {
    /**
     * 获取用户的菜单
     * @param userName
     * @return
     */
    List<String> getCurrentUserMenus(String userName);
}

新建ResouceMapper.xml

<?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.wujialiang.auth.mapper.ResouceMapper">
    <select id="getCurrentUserMenus" parameterType="java.lang.String" resultType="java.lang.String">
        select path from resource where userId = (select user.id from user  where UserName=#{userName} limit 1)
    </select>
</mapper>

修改UserService

package com.wujialiang.auth.service;

import com.wujialiang.auth.context.UserContext;
import com.wujialiang.auth.mapper.ResouceMapper;
import com.wujialiang.auth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ResouceMapper resouceMapper;

    public Boolean userLogin(String userName,String password){
        boolean isLogin = userMapper.userLogin(userName, password);
        return isLogin;
    }

    public void doSomething() {
        String currentUserName = UserContext.getCurrentUserName();
        System.out.println("Service层---当前用户登录名:" + currentUserName);
    }

    /**
     * 获取当前用户的菜单
     * @return
     */
    public List<String> getCurrentUserMenus(){
        String currentUserName = UserContext.getCurrentUserName();
        return resouceMapper.getCurrentUserMenus(currentUserName);
    }
}

修改UserController

package com.wujialiang.auth.controller;

import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.service.UserService;
import com.wujialiang.auth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 登录接口
     * @param user
     * @return
     */
    @PostMapping("/login")
    public String login(@RequestBody User user) {
        boolean isLogin = userService.userLogin(user.getUsername(), user.getPassword());
        if (isLogin) {
            // 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)
            return JwtUtil.generate(user.getUsername());
        }
        return "账号密码错误";
    }

    @GetMapping("/jwttest")
    public String api() {
        userService.doSomething();
        return "api成功返回数据";
    }

    /**
     * 获取当前用户的菜单
     * @return
     */
    @GetMapping("/menus")
    public List<String> getMenus() {
        return userService.getCurrentUserMenus();
    }
}

用户1获取
在这里插入图片描述
用户2获取菜单接口
在这里插入图片描述
对于页面的增删改不做讲解

RBAC模型

我这里为了方便演示所以没有设置过多的权限资源(就是导航菜单),所以整个权限系统用起来好像也挺方便的,不过一旦权限资源多了起来目前的设计有点捉襟见肘了。假设我们有100个权限资源,A用户要设置50个权限,BCD三个用户也要设置这同样的50个权限,那么我必须为每个用户都重复操作50下才行!这种需求还特别特别常见,比如销售部门的员工都拥有同样的权限,每新来一个员工我就得给其一步一步重复地去设置权限,并且我要是更改这个销售部门的权限,那么旗下所有员工的权限都得一一更改,极其繁琐。
现在我们的权限关系是和用户绑定的,所以每有一个新用户我们就得为其设置一套专属的权限。既然很多用户的权限都是相同的,那么我再封装一层出来,屏蔽用户和权限之间的关系不就搞定了。
修改资源表,添加角色表、用户角色关联关系、角色资源关联关系

create table resource(
    id int primary key AUTO_INCREMENT comment '主键',
    path varchar(255) comment 'URL路径',
    name varchar(255) comment 'URL名称'
);

insert into resource (path,name) value('/','首页');
insert into resource (path,name) value('/user/account','账户管理');
insert into resource (path,name) value('/user/role','角色管理');
insert into resource (path,name) value('/data','数据管理');

create table role(
    id int primary key AUTO_INCREMENT comment '主键',
    name varchar(255) comment '角色名称'
);

insert into role (name) value('超级管理员');
insert into role (name) value('数据管理员');

create table role_resource(
    roleId int comment '角色id',
    resourceId int comment '资源id'
);

-- 超级管理员
insert into role_resource (roleId,resourceId) value(1,1);
insert into role_resource (roleId,resourceId) value(1,2);
insert into role_resource (roleId,resourceId) value(1,3);
insert into role_resource (roleId,resourceId) value(1,4);
-- 数据管理员
insert into role_resource (roleId,resourceId) value(2,1);
insert into role_resource (roleId,resourceId) value(2,4);

create table user_role(
    roleId int comment '角色id',
    userId int comment '用户id'
);
-- 用户1超级管理员
insert into user_role (roleId,userId) value(1,1);
-- 用户2
insert into user_role (roleId,userId) value(2,2);

实体

@Data
public class Resource {
    private int id;
    private String name;
    private String path;
}

@Data
public class Role {
    private int id;
    private String name;
}

@Data
public class RoleResource {
    private int roleId;
    private int resourceId;
}

@Data
public class UserRole {
    private int roleId;
    private int userId;
}

修改ResouceMapper以及相关的service和controller

@Repository
@Mapper
public interface ResouceMapper extends BaseMapper<Resource> {
    /**
     * 获取用户的菜单
     * @param userName
     * @return
     */
    List<Resource> getCurrentUserMenus(String userName);
}

修改ResouceMapper.xml

<?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.wujialiang.auth.mapper.ResouceMapper">
    <select id="getCurrentUserMenus" parameterType="java.lang.String" resultType="com.wujialiang.auth.entity.Resource">
        select t2.id,t2.name,t2.path from role_resource t1
            left join resource t2 on t2.id=t1.resourceId
        where t1.roleId =
              (select t3.roleId from user_role t3 where t3.userId=
                                                        (select t4.id from user t4 where t4.UserName=#{userName} limit 1)
            limit 1)
    </select>
</mapper>

用户1超级管理员权限如下
在这里插入图片描述

用户2数据管理员权限如下
在这里插入图片描述
后续请看下篇
springboot权限验证学习-下

参考

https://www.cnblogs.com/RudeCrab/p/14251274.html
https://www.cnblogs.com/RudeCrab/p/14251154.html
https://blog.csdn.net/qq1910506668/article/details/136608184

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

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

相关文章

【MyBatis】初步解析MyBatis:实现数据库交互与关系映射的全面指南

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【MyBatis】初步解析MyBatis&#xff1a;实现数据库交互与关系映射的全面指南 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 前言什么是MyBatis?一. MyBa…

六.音视频编辑-创建视频过渡-概述

引言 目前我的应用已经实现了视频的编辑&#xff0c;音频的混合处理。随着时间的推进&#xff0c;两个不同场景的视频快速的切换&#xff0c;其中没有任何过渡效果。通常画面在时间轴上出现明显的变化时&#xff0c;两个场景间会使用一些动画的过渡效果。比如渐隐&#xff0c;…

麦肯锡报告:《在实现量子优势方面稳步推进》

2024年4月24日&#xff0c;麦肯锡一年一度的Quantum Technology Monitor发布了其最新的2024年研究报告&#xff0c;提供对全球量子技术&#xff08;QT&#xff09;、投资、生态系统等发展现状的见解。 此次&#xff0c;麦肯锡为第三届年度Quantum Technology Monitor报告所做的…

[C++]22:C++11_part_one

C11 一.列表初始化&#xff1a;1.{}初始化&#xff1a;2.C11扩大了列表初始化的范围&#xff1a;3.std::initializer_list1.简单类型的列表初始化&#xff1a;2.复杂类型的列表初始化3.实现vector的列表初始化4.实现list的列表初始化&#xff1a;5.不支持列表初始化&#xff1a…

多用户商城系统哪个好,2024多用户商城系统这样选

在2024年选择适合的多用户商城系统是一项至关重要的决策&#xff0c;因为一个优秀的商城系统不仅可以提升用户体验&#xff0c;还能够帮助企业实现业务目标并取得长期成功。然而&#xff0c;在众多的选择中挑选出最适合的一个并不容易&#xff0c;需要综合考虑各种因素&#xf…

网页模版如何用

现在的网页模版已经得到了许多人的喜爱和使用。随着人们对互联网的需求不断增加&#xff0c;更多的公司和组织需要拥有自己的网站&#xff0c;以推广他们的品牌和服务。而网页模版为他们提供了一个简单而高效的方法来创建自己的网站。 网页模版是预先设计好的网站模板&#xff…

docker容器通俗理解

前言 如果大家没使用过Docker,就在电脑上下载一个VMware Workstation Pro&#xff0c;创建一个虚拟机安装一个windows操作一下感受一下,为什么我的电脑上还以再安装一台windows主机&#xff1f;其实你可以理解为Docker就是Linux系统的一个虚拟机软件。 我的Windows也可以安装…

(学习日记)2024.05.08:UCOSIII第六十二节:常用的结构体(os.h文件)第一部分

之前的章节都是针对某个或某些知识点进行的专项讲解&#xff0c;重点在功能和代码解释。 回到最初开始学μC/OS-III系统时&#xff0c;当时就定下了一个目标&#xff0c;不仅要读懂&#xff0c;还要读透&#xff0c;改造成更适合中国宝宝体质的使用方式。在学完野火的教程后&a…

Java基础教程- 1 Java 简介

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 1 Java 简介 1.1 Java语言概述 1.1.1 Java是什么 &#xff08;了解一下即可&#xff0c;对学习没影响&#xff0c;总得了解一下它是怎么来滴~&#xff09; Java是由Sun公司&#xff08;已…

五一景点预约怎么预约 预约时间用备忘录设置提醒不怕错过

五一小长假即将来临&#xff0c;相信很多人和我一样&#xff0c;已经跃跃欲试&#xff0c;准备踏上旅途&#xff0c;去探索那些心仪已久的风景名胜。但在这个旅游高峰期&#xff0c;不少热门景点都需要提前预约购票。那么&#xff0c;怎么预约才能确保顺利游览呢&#xff1f; …

在智慧城市的建设中智能电表发挥什么作用

在智慧城市的建设中&#xff0c;智能电表扮演着至关重要的角色。智慧城市是一个利用信息技术手段提升城市运行效率和质量的新型城市模式&#xff0c;旨在通过信息和通信技术的应用&#xff0c;提高城市管理、公共服务、环境保护等方面的质量和效率&#xff0c;促进城市的可持续…

金价大跳水,美梦变噩梦!2024真正适合普通人的靠谱创业项目!2024适合30-40岁轻资产小生意

4月22日晚间&#xff0c;向上“狂飙”了一个多月的金价突然就“大跳水”。当日&#xff0c;每克金价均下调14块。在这次跳水中&#xff0c;有人欢喜有人愁&#xff1a;有投资者自报做空金价一夜狂赚14万&#xff0c;也有投资者哭诉&#xff0c;头晚进货到早上就净亏损2万&#…

中电金信:GienTech动态| 获奖、合作、与伙伴共谋数字化转型…

—— —— GienTech动态 —— —— 中电金信携“源启”亮相第十二届中国电子信息博览会 4月11日&#xff0c;为期三天的“第十二届中国电子信息博览会”在深圳顺利闭幕。中国电子信息博览会是中国规模最大、最具国际影响力的电子信息产业盛会之一。本届大会以“全球视野&#x…

AI预测福彩3D第9套算法实战化测试第6弹2024年4月28日第6次测试

今天继续进行新算法的测试&#xff0c;今天是第6次测试。好了&#xff0c;废话不多说了&#xff0c;直接上图上结果。 2024年4月28日福彩3D预测结果 6码定位方案如下&#xff1a; 百位&#xff1a;5、4、9、3、1、0 十位&#xff1a;2、3、5、6、1、7 个位&#xff1a;4、5、0、…

Goby 漏洞发布|禅道 /api.php/v1/users 未授权访问漏洞

漏洞名称&#xff1a;禅道 /api.php/v1/users 未授权访问漏洞 English Name&#xff1a;Zen Road /api.php/v1/users Unauthorized Access Vulnerability CVSS core: 9.8 影响资产数&#xff1a;69265 漏洞描述&#xff1a; 禅道是一款开源的项目管理软件&#xff0c;旨在…

Leetcode—1017. 负二进制转换【中等】(string列表初始化、反向迭代器)

2024每日刷题&#xff08;120&#xff09; Leetcode—1017. 负二进制转换 实现代码 class Solution { public:string baseNeg2(int n) {string ans;while(n ! 0) {ans to_string(n & 1);n -(n >> 1);}return ans.empty() ? "0": string{ans.rbegin(),…

Linux安装Matlab运行时

一般而言&#xff0c;安装Matlab的linux系统是带桌面版的&#xff0c;如果没带&#xff0c;不在本教程范围内。 一、下载Matlab 下载地址&#xff1a;MATLAB Runtime - MATLAB Compiler - MATLAB 本教程使用R2020b(9.9) 二、linux系统中进行解压 将zip传入linux系统&#xf…

vue2主体页面进行拆分

目录 一.组件化 二.新建Header.vue页面 三.Aside.vue代码 四.Main.vue代码如下 五.Home.vue代码如下 六.index.js代码如下&#xff1a; 七.项目效果图 在Vue.js 2中&#xff0c;将主体页面进行拆分是一种常见的做法&#xff0c;它有助于提高代码的可维护性和可读性。页面…

Halcon 3D 使用3D ROI截取模型

Halcon 3D 使用3D ROI截取模型 链接:https://pan.baidu.com/s/1UfFyZ6y-EFq9jy0T_DTJGA 提取码:ewdi * 1.读取图片 ****************

新装电脑Flutter环境部署坑汇总(持续更新)

1.本地安装&#xff0c;安装fvm的坑 本人电脑使用windows &#xff0c;安装fvm则一般使用choco安装&#xff0c;那么首先需要安装choco,打开powershell/或者cmd运行以下命令&#xff1a; Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager…