spring boot集成mybatis和springsecurity实现权限控制功能

上一篇已经实现了登录认证功能,这一篇继续实现权限控制功能,文中代码只贴出来和上一篇不一样的修改的地方,完整代码可结合上一篇一起整理spring boot集成mybatis和springsecurity实现登录认证功能-CSDN博客

数据库建表

权限控制的意思就是根据用户角色的不同,赋予不同的访问权限,比如管理员可以对数据进行增删改查操作,而普通用户只能查询数据。根据网上大部分文章的说法,最好的授权方式叫RABC,意思是基于资源的访问控制,详见SpringSecurity授权-CSDN博客

这种方式会创建5个数据库表,分别为用户表,角色表,权限表,用户角色关系表,角色权限关系表,他们之间存在一些对应关系,每次根据用户名可以查询到这个用户的角色和权限信息,我也不懂为啥要把授权相关的表拆成5个,按照我自己的理解,就是把所有用户、角色、权限信息放在一张表里也不是不行,但是既然大家都建议这么做,肯定是有道理的,所以我就按照主流方式来实现权限控制的数据库表创建

数据库建表和数据SQL如下:

-- test.permission definition权限信息表 
CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='权限信息表'; 
INSERT INTO test.permission (name,permission) VALUES ('selectinfo','url1'), ('insertinfo','url2'), ('updateinfo','url3'), ('deleteinfo','url4'); 
-- test.`role` definition角色信息表 
CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `description` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; 
INSERT INTO test.`role` (name,description) VALUES ('admin','admin'), ('user','user');
-- test.`user` definition用户信息表 
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, `role` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; 
INSERT INTO test.`user` (username,password,permission,`role`) VALUES ('aaa','$2a$10$m3tlFCB8IiZzm6dj7Q58KuD9FnkZI8M/1scJ2dZBZwiToFpgRMZCe',NULL,NULL), ('bbb','$2a$10$Wwfn31iTecY0n66LHXNSeeiPTcr.ZJsDHVuvnHR150rMqgJXugio2',NULL,NULL), ('ccc','1234',NULL,NULL); 
-- test.role_permission definition角色权限关系表 
CREATE TABLE `role_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` varchar(255) DEFAULT NULL, `permission_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
INSERT INTO test.role_permission (role_id,permission_id) VALUES ('1','1'), ('1','2'), ('1','3'), ('1','4'), ('2','1'), ('2','3'); 
-- test.user_role definition用户角色关系表 
CREATE TABLE `user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `role_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 
INSERT INTO test.user_role (user_id,role_id) VALUES ('1','1'), ('1','2'), ('2','2');

所有和5个数据库表相关的dao、dto、service、mapper文件可参考上一篇user表编写,这里不在贴出源码

代码修改
1,springsecurity配置类

WebSecurityConfig添加支持权限控制的注解如下:

其中第一个配置项perPostEnable表示开启@PreAuthroize注解,是方法或类级别的注解,只需要在方法上添加@PreAuthroize注解即可,可以在资源被访问之前判断用户权限,是比较常用的方法,比如

图中注解就表示只有具备url1权限的用户才能执行方法queryName()。

第二个配置项securedEnable是专门用于判断是否具有角色的,可以在方法或者类上使用,参数需要以ROLE_开头,本文未使用所以忽略

2,UserDetailsServiceImpl

该方法实现了框架的UserDetailsService接口,并复写了loadUserByUsername方法,用于查询数据库的用户信息,添加权限控制功能后,需要同时查询用户的权限信息,代码如下:

package com.sgp.ss.security;

import com.sgp.ss.dao.IUserMapper;
import com.sgp.ss.domain.entity.PermissionEntity;
import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 java.util.ArrayList;
import java.util.List;

/**
 * @author shanguangpu
 * @date 2023/11/30 15:50
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    IUserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            UserEntity userEntityByName = userMapper.getUserEntityByName(username);
            if (userEntityByName == null){
                throw new UsernameNotFoundException("用户"+username+"不存在");
            }

            List<PermissionEntity> permissionList = userMapper.findPermissionByUsername(username);//查询用户权限信息
            List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
            for (PermissionEntity p : permissionList){
                grantedAuthorities.add(new SimpleGrantedAuthority(p.getPermission()));
            }
            return new LoginUser(userEntityByName,grantedAuthorities);

            //测试用
//            List<String> list = new ArrayList<>(Arrays.asList("test"));
//            List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
//            for(String s:list){
//                grantedAuthorities.add(new SimpleGrantedAuthority(s));
//            }
//            return new LoginUser(userEntityByName,list);

        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

其中方法findPermissionByUsername()是我们新加的,需要在mapper文件中编写SQL,实现根据用户名查询用户权限的功能,这个SQL语句可以按照下述格式直接写,如果数据库表名称不同,只需要修改表名就行

<select id="findPermissionByUsername" resultType="com.sgp.ss.domain.entity.PermissionEntity" parameterType="java.lang.String">
        <if test="_parameter != null">
            SELECT DISTINCT permission.id,permission.name,permission.permission FROM
            user
            LEFT JOIN user_role on user.id = user_role.user_id
            LEFT JOIN role on user_role.role_id = role.id
            LEFT JOIN role_permission on role.id = role_permission.role_id
            LEFT JOIN permission on role_permission.permission_id = permission.id
            where user.username = #{username} and 1 = 1
            <!--select <include refid="UserEntityColumns"/> from user-->
            <!--where username = #{username} and 1 = 1-->
        </if>
    </select>
3,LoginUser 

从上述代码中可以看出,当我们从数据库查询完用户权限信息后,框架会将它封装到用户对象UserDetails中,代码如下:

package com.sgp.ss.security;

import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

/**
 * @author shanguangpu
 * @date 2023/11/30 15:58
 */
public class LoginUser extends UserEntity implements UserDetails {

    Collection<? extends GrantedAuthority> authorities;

    private List<String> permissions = new ArrayList<>();

    public LoginUser(UserEntity userEntity){
        if (null != userEntity){
            this.setUsername(userEntity.getUsername());
            this.setPassword(userEntity.getPassword());
        }
    }

    public LoginUser(UserEntity userEntity, Collection<? extends GrantedAuthority> authorities){
        this.setId(userEntity.getId());
        this.setUsername(userEntity.getUsername());
        this.setPassword(userEntity.getPassword());
//        permissions.add(userEntity.getPermission());
        this.authorities = authorities;
    }

    public LoginUser(UserEntity userEntity, List<String> permissions){
        this.setId(userEntity.getId());
        this.setUsername(userEntity.getUsername());
        this.setPassword(userEntity.getPassword());
        this.permissions = permissions;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (authorities != null){
            return authorities;
        }
        authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        return authorities;
    }

    /**
     * 账户是否过期
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 账户是否被锁定
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 证书是否过期
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 账户是否有效
     * @return
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}
4,JwtAuthenticationTokenFilter

下次访问资源只需要从token中解析出权限信息即可,所以需要在JWT过滤器中添加token解析步骤,并交给框架进行权限比对,存在上下文中

package com.sgp.ss.security;

import com.sgp.ss.domain.entity.UserEntity;
import com.sgp.ss.service.UserEntityService;
import com.sgp.ss.util.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * @author shanguangpu
 * @date 2023/12/8 15:29
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserEntityService userEntityService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

//    @Autowired
//    private JwtProperties jwtProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //获取token信息
        final String authorization = request.getHeader("token");

        String name = null;
        String authToken = null;

        if (!StringUtils.isEmpty(authorization)) {
            authToken = authorization.replace("Authorization", "");
            try {
                name = jwtTokenUtil.getUsernameFromToken(authToken);
            } catch (ExpiredJwtException e){
                e.printStackTrace();
            }
        }
        if (name != null && SecurityContextHolder.getContext().getAuthentication() == null){
//            if (jwtTokenUtil.isTokenValid(name, authToken)){
            UserEntity userEntityByName = userEntityService.getUserEntityByName(name);
            List<GrantedAuthority> authorityFromToken = jwtTokenUtil.getAuthorityFromToken(authToken);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userEntityByName, null, authorityFromToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);
//            }
        }
        filterChain.doFilter(request, response);

    }

    public JwtAuthenticationTokenFilter(){
        super();
    }
}
5,JWT工具类

然后在JWT工具类中实现权限获取方法,完整代码如下:

package com.sgp.ss.util;

import com.sgp.ss.security.LoginUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author shanguangpu
 * @date 2023/2/22 10:42
 */
@Component
public class JwtTokenUtil implements Serializable {

    private static final Logger LOG = LoggerFactory.getLogger(JwtTokenUtil.class);

    private static final long serialVersionUID = -1264536648286114018L;

    private Clock clock = DefaultClock.INSTANCE;

    private Map<String, String> tokenMap = new ConcurrentHashMap<>(32);

    public String generateToken(LoginUser loginUser){
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", loginUser.getUsername());
        claims.put("authority", loginUser.getAuthorities());
        String token = generateToken(claims, loginUser.getId() + "");
        if (! StringUtils.isEmpty(token)){

        }
        return token;
    }

    /**
     * 生成token
     * @param claims
     * @param subject
     * @return
     */
    private String generateToken(Map<String, Object> claims, String subject) {
        final Date createdDate = clock.now();
        final Date expirationDate = generateExpirationDate(createdDate);
        return Jwts.builder()
                // 自定义属性
                .setClaims(claims)
                .setSubject(subject)
                // 创建时间
                .setIssuedAt(createdDate)
                // 过期时间
                .setExpiration(expirationDate)
                // 签名算法及秘钥
                .signWith(SignatureAlgorithm.HS512, "sgpsgp")
                .compact();
    }

    /**
     * 设置过期时间,以当前时间加上毫秒数计算,该方法中设置的过期时间是6小时
     * @param createdDate
     * @return
     */
    private Date generateExpirationDate(Date createdDate) {
        return new Date(createdDate.getTime() + 21600 * 1000);
    }

    // 从得到的令牌里面获取用户ID
    public Integer getUserIdFromToken(String token) {
        Integer id = null;
        try {
            final Claims claims = getClaimsFromToken(token);
            id = Integer.parseInt(claims.getSubject());
            return id;
        } catch (Exception e) {
        }
        return id;
    }

    // 从得到的令牌里面获取用户名
    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = (String) claims.get("username");
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    // 从得到的令牌里面获取权限
    public List<GrantedAuthority> getAuthorityFromToken(String token) {
        List<GrantedAuthority> list = new ArrayList<>();
        try {
            final Claims claims = getClaimsFromToken(token);
            StringBuffer sb = new StringBuffer();
            List<LinkedHashMap> authority2 = (List<LinkedHashMap>) claims.get("authority");
            for (LinkedHashMap s : authority2){
                String authority = (String) s.get("authority");
                sb.append(authority+",");
                list.add(new SimpleGrantedAuthority(authority));
            }
            LOG.info("当前用户名称是:{},权限url为:{}",claims.get("username"),sb.subSequence(0,sb.length()-1));
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    // 从得到的令牌里面获取创建时间
    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = claims.getIssuedAt();
        } catch (Exception e) {
            created = null;
        }
        return created;
    }
    // 从得到的令牌里面获取过期时间
    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey("sgpsgp")
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }


    /**
     * 检查token是否过期
     * @param token
     * @return
     */
    public Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        boolean flag = expiration.before(new Date());
        if(flag) {
            String username = getUsernameFromToken(token);
            if(username != null) {
                tokenMap.remove(username);
            }
        }
        return flag;
    }

    public Boolean isTokenValid(String username, String token) {
        if(tokenMap.get(username) != null && tokenMap.get(username).equals(token.trim())) {
            return !isTokenExpired(token);
        }
        return false;
    }

    public Boolean isTokenValid(int id, String token) {
        if (tokenMap.get(id) != null && tokenMap.get(id).equals(token.trim())) {
            return !isTokenExpired(token);
        }
        return false;
    }

    public void deleteToken(String token) {
        String username = getUsernameFromToken(token);
        if(username != null) {
            tokenMap.remove(username);
        }
    }


    }

}
6,controller

controller文件中可以多写几个方法,分别给与不同的权限,代码如下:

package com.sgp.ss.controller;

import com.sgp.ss.dao.*;
import com.sgp.ss.domain.dto.data.*;
import com.sgp.ss.domain.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author shanguangpu
 * @date 2023/12/12 14:59
 */
@RestController
public class index {

    @Autowired
    private IUserMapper userMapper;

    @Autowired
    private IRoleMapper roleMapper;

    @Autowired
    private IPermissionMapper permissionMapper;

    @Autowired
    private IUserRoleMapper userRoleMapper;

    @Autowired
    private IRolePermissionMapper rolePermissionMapper;

    @GetMapping(value = "hello")
    public String request(){
        System.out.println("hello word");
        return "hello success";
    }

    @PostMapping(value = "select")
    @PreAuthorize("hasAuthority('url2')")
    public String selectInfo(){
        List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());
        return userEntities.toString();
    }


    @PostMapping(value = "queryName")
    @PreAuthorize("hasAuthority('url1')")
    public String queryName(){

        List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());
        int i = 1;
        for (UserEntity userEntity : userEntities){
            String username = userEntity.getUsername();
            System.out.println("name"+i+" is : "+username);
            i++;
        }
        return "query finish";

    }

    @PostMapping(value = "queryTable")
    public String queryRoleAndPermission(){
        List<RoleEntity> roleEntities = roleMapper.queryRoleEntityList(new RoleDto());
        for (RoleEntity roleEntity : roleEntities){
            System.out.println(roleEntity.getId()+","+roleEntity.getName()+","+roleEntity.getDescription());
        }
        List<PermissionEntity> permissionEntities = permissionMapper.queryPermissionEntityList(new PermissionDto());
        for (PermissionEntity permissionEntity : permissionEntities){
            System.out.println(permissionEntity.getId()+","+permissionEntity.getName()+","+permissionEntity.getPermission());
        }
        List<RolePermissionEntity> rolePermissionEntities = rolePermissionMapper.queryRolePermissionEntityList(new RolePermissionDto());
        for (RolePermissionEntity rolePermissionEntity : rolePermissionEntities){
            System.out.println(rolePermissionEntity.getId()+","+rolePermissionEntity.getPermission_id()+","+rolePermissionEntity.getRole_id());
        }
        List<UserRoleEntity> userRoleEntities = userRoleMapper.queryUserRoleEntityList(new UserRoleDto());
        for (UserRoleEntity userRoleEntity: userRoleEntities){
            System.out.println(userRoleEntity.getId()+","+userRoleEntity.getRole_id()+","+userRoleEntity.getUser_id());
        }

        List<PermissionEntity> bbb = userMapper.findPermissionByUsername("bbb");
        for (PermissionEntity p : bbb){
            System.out.println(p.getId()+","+p.getName()+","+p.getPermission());
        }

        return "success";

    }

}
postman测试
1,获取token

首先我们通过用户名获取到token

2,具备权限测试

然后携带这个token,访问接口/queryTable,这个接口可以查询出数据库里所有相关的数据,以及用户bbb的权限列表打印在IDEA控制台

可以看到,用户bbb具备访问url1和url3的权限,那么我们继续测试接口/queryName,这个接口上方注解标注了需要具备url1权限才能访问,如下

3,不具备权限测试

然后测试接口/select,该接口上方注解表示只有具备url2的权限才能访问,用户bbb不具备这个权限,所以访问会失败,如下

至此,权限控制功能测试完毕

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PopChar for Mac 特殊字符输入工具

PopChar Popchar表情输入&#xff0c;一款专门输入特殊字符的软件&#xff0c;让查找和输入特殊字符变得简单快捷方便&#xff0c;可以快速搜索查找表情&#xff0c;还可以将经常发的表情进行收藏&#xff0c;方便下次直接发送&#xff0c;让聊天更加充满快乐&#xff01; 资源…

智能优化算法应用:基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.水基湍流算法4.实验参数设定5.算法结果6.…

基本shell功能实现(exec系列程序替换函数练习)

shell 功能描述思路介绍1.实现常驻进程功能2.实现命令读取功能3. 实现命令解析功能4.实现子进程执行命令功能5.完善功能 补充内容让父进程运行内置命令实现子进程能够获得父进程的环境变量功能&#xff08;export命令&#xff09;shell实现重定向功能 全部代码如下&#xff1a;…

CentOS7 安装 DockerCompose

目录 一、安装Docker 二、安装步骤 2.1 卸载 2.2 安装docker 2.3 启动docker 2.4 配置镜像加速器 一、安装Docker Docker 分为 CE 和 EE 两大版本。 CE 即社区版(免费&#xff0c;支持周期7个月)EE 即企业版强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月…

Linux之进程(五)(进程控制)

目录 一、进程创建 1、fork函数创建进程 2、fork函数的返回值 3、fork常规用法 4、fork调用失败的原因 二、进程终止 1、进程终止的方式 2、进程退出码 3、进程的退出方法 三、进程等待 1、进程等待的必要性 2、wait函数 3、waitpid函数 四、进程程序替换 1、概念…

Java如何将中文转化为拼音

Java中可以使用第三方库pinyin4j来实现中文转拼音。 首先&#xff0c;需要引入pinyin4j的jar包&#xff0c;可以在pinyin4j的官方网站&#xff08;http://pinyin4j.sourceforge.net/&#xff09;下载&#xff0c;也可以通过Maven引入。 Maven引入依赖&#xff1a; <depend…

aws配置以及下载 spaceNet6 数据集

一&#xff1a;注册亚马逊账号 注册的时候&#xff0c;唯一需要注意的是信用卡绑定&#xff0c;这个可以去淘宝买&#xff0c;搜索aws匿名卡。 注册完记得点击登录&#xff0c;记录一下自己的账户ID哦&#xff01; 二&#xff1a;登录自己的aws账号 2.1 首先创建一个用户 首…

数字化指南:数据可视化助力网店腾飞

数据可视化对于网店经营具有重要意义。它不仅仅是一种呈现数据的方式&#xff0c;更是提升网店运营效率和业绩的有力工具。下面我就以可视化从业者的角度&#xff0c;简单聊聊数据可视化能够给网店经营带来的帮助。 在网店经营中&#xff0c;数据可视化能够带来多方面的帮助。…

【PHP入门】2.2 流程控制

-流程控制- 流程控制&#xff1a;代码执行的方向 2.2.1控制分类 顺序结构&#xff1a;代码从上往下&#xff0c;顺序执行。&#xff08;代码执行的最基本结构&#xff09; 分支结构&#xff1a;给定一个条件&#xff0c;同时有多种可执行代码&#xff08;块&#xff09;&am…

基于MLP完成CIFAR-10数据集和UCI wine数据集的分类

基于MLP完成CIFAR-10数据集和UCI wine数据集的分类&#xff0c;使用到了sklearn和tensorflow&#xff0c;并对图片分类进行了数据可视化展示 数据集介绍 UCI wine数据集&#xff1a; http://archive.ics.uci.edu/dataset/109/wine 这些数据是对意大利同一地区种植的葡萄酒进…

Ubuntu 常用命令之 echo 命令用法介绍

echo 是一个在 Ubuntu 系统下常用的命令&#xff0c;主要用于在终端输出字符串或者变量。 echo 的基本语法 echo [option] [string]echo 命令的参数包括 -n&#xff1a;不输出结尾的换行符。-e&#xff1a;启用反斜杠转义字符。-E&#xff1a;禁用反斜杠转义&#xff08;这是…

【论文解读】Efficient SAO Coding Algorithm for x265 Encoder

时间&#xff1a;2015年 级别&#xff1a;IEEE 机构&#xff1a;上海交通大学 摘要 x265是一款开源的HEVC编码器&#xff0c;采用了多种优化技术&#xff0c;具有较快的编码速度和优良的编码性能。作为HEVC的一项关键技术&#xff0c;x265还采用了样本自适应偏移(sample adap…

c++ qt 模态框和阻拦器 优先级 问题 修复 已解决

在c项目中。有 加载动画 和 模态框提醒的功能, 导致发生一个问题&#xff0c;有提示框的时候&#xff0c;动画也停止&#xff0c;必须点击 按钮 所有代码才能有效。 解决办法 谨慎使用 deleteLater,因为和模态框拦截有冲突, 使用 隐藏 或者 删除指针。 deleteLater 使用逻辑是 …

自动气象监测站助力生活生产

随着科技的发展&#xff0c;我们的生活和生产方式正在发生着日新月异的变化。其中&#xff0c;WX-CQ12 自动气象监测站作为一项气象监测设备&#xff0c;正在发挥着越来越重要的作用。它不仅为我们提供了更加准确、实时的天气信息&#xff0c;还为农业、交通、旅游等领域提供了…

全新「机械手」算法:辅助花式抓杯子,GTX 1650实现150fps推断

新方法结合扩散模型和强化学习&#xff0c;将抓取问题分解为「如何抓」以及「何时抓」&#xff0c;平价显卡即可实现实时交互。 手是人类与世界交互的重要部分&#xff0c;手的缺失&#xff08;如上肢残障&#xff09;会大大影响人类的正常生活。 北京大学董豪团队通过将扩散模…

stm32学习总结:4、Proteus8+STM32CubeMX+MDK仿真串口收发

stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发 文章目录 stm32学习总结&#xff1a;4、Proteus8STM32CubeMXMDK仿真串口收发一、前言二、资料收集三、STM32CubeMX配置串口1、配置开启USART12、设置usart中断优先级3、配置外设独立生成.c和.h 四、MDK串口收发…

VS ASP.Net Core项目还原Packages包到本地(解决服务器没有网无法重新生成的问题)

问题背景 ASP.Net Core MVC项目&#xff0c;无法重新生成。 现场服务器没有网,放上去的代码无法通过nuget还原包到服务器&#xff0c;导致无法编译无法运行。 解决办法 将Packages还原到本机&#xff08;有网&#xff09;&#xff0c;然后再将代码放到服务器运行。 在有网的…

内网渗透测试基础——分析域内网段划分情况及拓扑结构

内网渗透测试基础——分析域内网段划分情况及拓扑结构 ​ 掌握了内网的相关信息后&#xff0c; 渗透测试人员可以分析目标网络的结构和安全防御策略&#xff0c;获取网段信息、各部门的IP地址段&#xff0c;并尝试绘制内网的拓扑结构图。当然&#xff0c;渗透测试人员无法了解…

深入了解Java中的锁机制

目录 1. synchronized关键字 1.1 基本概念 1.2 内置锁 1.3 限制 2. ReentrantLock 2.1 概述 2.2 公平性与非公平性 2.3 条件变量 3. 读写锁&#xff08;ReadWriteLock&#xff09; 3.1 概念 3.2 适用场景 4. StampedLock 4.1 概述 4.2 乐观读与悲观读 4.3 适用场…

仓储1代电子标签接口文档

标签注册 仓储1代注册 侧面按钮连按三次&#xff0c; 注册成功&#xff1a;红灯变绿灯 查询电子标签信息接口 接口地址&#xff1a;192.168.1.200/wms/associate/getTagsMsg 请求类型&#xff1a;multipart/form-data 请求方式&#xff1a;get 接口备注&#xff1a;暂无描…