shiro会话管理和加密

news2024/10/28 18:08:00

Shiro 会话管理和加密
会话管理
缓存
加密
会话管理
Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对 Web 的透明支持,SSO 单点登录的支持等特性。

会话相关API

Subject.getSession():获取会话,等价于 Subject.getSession(true),即如果当前没有创建 session 对象会创建一个;Subject.getSession(false),如果当前没有创建 session 对象则返回 null。
session.getId():获取当前会话的唯一标识。

session.setAttribute(key,val) :设置会话属性。
session.getAttribute(key) :获取会话属性。
session.removeAttribute(key):删除会话属性。
SessionDAO
Shiro 提供 SessionDao 用于会话持久化。提供 CRUD 操作。

AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话 ID 等。
CachingSessionDAO 提供了对开发者透明的会话缓存的功能,需要设置相应的 CacheManager。
MemorySessionDAO 直接在内存中进行会话维护。
EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用 ConcurrentHashMap 保存缓存的会话。
在实际开发中,如果要用到 SessionDAO 组件,可以自定义类实现自 EnterpriseCacheSessionDAO 类,为其注入 sessionIdGenerator 属性,如果用到缓存的话还可以注入一个缓存的名字。最后将这个 SesionDAO 组件注入给 SessionManager(会话管理器),最后将 SessionManager 配置给 SecurityManager。

会话使用
建议在开发中,Controller 层使用原生的 HttpSession 对象,在 Service 层中使用 Shiro 提供的 Session 对象。如果在 Service 层中使用 HttpSession 对象,那么属于侵入式,并不建议这么做。Shiro 提供的 Session 能够很好的解决这个问题。

那么,问题来了,两种方式获取的 session 是否相同呢?

在 Controller 中,通过 request.getSession() 获取会话 session ,该 session 到底来源于
ServletRequest 还是由 Shiro 管理并创建的会话,主要由安全管理器 SecurityManager 和
SessionManager 会话管理器决定。

在使用默认 SessionManager 会话管理器的情况下,不管是通过 request.getSession() 或者 subject.getSession() 获取到 session,操作 session,两者都是等价的,请大家放心使用!

缓存

使用第三方的 shiro-redis 集成 redis 实现缓存

  1. 添加依赖
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.1.0</version>
</dependency>

  1. application.poperties 配置文件中添加 Redis 配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis 服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=hxy
#连接池最大连接数(使用负值表示没有限制)默认8
spring.redis.lettuce.pool.max-active=8
#连接池最大阳塞等待时间(使用负值表示没有限制)默认-1
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接默认8
spring.redis.lettuce.pool.max-idle=8
#连接池中的最小空闲连接默认日
spring.redis.lettuce.pool.min-idle=0
#Redis服务超时时间
spring.redis.timeout=5000

 

  1. ShiroConfig.java
package com.bdqn.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.bdqn.pojo.Right;
import com.bdqn.service.RightService;
import com.bdqn.shiro.MyShiroRealm;
import jakarta.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * ShiroConfig
 */
@Configuration
public class ShiroConfig {

    // 注入Redis参数,从application.yml获得
    @Value("${spring.data.redis.host}")
    private String host;
    @Value("${spring.data.redis.port}")
    private int port;
    @Value("${spring.data.redis.password}")
    private String password;
    @Value("${spring.data.redis.connect-timeout}")
    private int timeout;

    @Resource
    private RightService rightService;

    /**
     * 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个 bean(DefaultAdvisorAutoProxyCreator 和 AuthorizationAttributeSourceAdvisor)
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启AOP注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 验证匹配规则
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用 md5 算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数:意为加密几次
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }

    /**
     * 自定义Realm(基于数据库)
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm shiroRealm = new MyShiroRealm();
        // 设置启用缓存,并设置缓存名称
        shiroRealm.setCachingEnabled(true);
        shiroRealm.setAuthorizationCachingEnabled(true);
        shiroRealm.setAuthorizationCacheName("authorization");
        // 设置凭证(密码)匹配器
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    /**
     * Thymeleaf页面上使用shiro标签
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * Redis管理器
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host + ":" + port); // #shiro-redis v3.3.1
        redisManager.setPassword(password);
        redisManager.setTimeout(timeout);
        return redisManager;
    }

    /**
     * 缓存管理器
     */
    @Bean
    public CacheManager shiroCacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setRedisManager(redisManager());
        // 缓存名称
        cacheManager.setPrincipalIdFieldName("usrName");
        // 缓存有效时间
        cacheManager.setExpire(1800);
        return cacheManager;
    }

    /**
     * 会话持久化操作
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

    /**
     * 安全管理器SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入自定义的Realm
        securityManager.setRealm(myShiroRealm());
        // 注入缓存管理器
        securityManager.setCacheManager(shiroCacheManager());
        // 注入会话管理器
        securityManager.setSessionManager(sessionManager());
        // Shiro2.0.1新版本需要SecurityUtils设置SecurityManager,否则报错(org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code)
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    /**
     * 会话管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * Shiro过滤器:权限验证
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean();
        // 注入SecurityManager
        shiroFilterFactory.setSecurityManager(securityManager);
        // 权限验证:使用 Filter 控制资源(URL)的访问
        shiroFilterFactory.setLoginUrl("/login"); // 登录页面URL
        shiroFilterFactory.setSuccessUrl("/main"); // 登录成功URL
        shiroFilterFactory.setUnauthorizedUrl("/403"); // 没有权限跳转403页面

        // 权限配置集合,必须使用LinkedHashMap(有序集合)
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // 静态资源授权
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/localcss/**", "anon");
        filterChainDefinitionMap.put("/localjs/**", "anon");

        // API接口请求授权
        filterChainDefinitionMap.put("/api/**", "anon");

        // 登录退出页面设置
        filterChainDefinitionMap.put("/login", "anon"); // 点击登录按钮时放行
        filterChainDefinitionMap.put("/logout", "logout"); // 点击退出按钮时放行

        // 配置需要特定权限才能访问的资源(URL)
        // 静态授权:包括全部需要特定权限才能访问的资源(URL)
        /*
        filterChainDefinitionMap.put("/user/list", "perms[用户列表]");
        filterChainDefinitionMap.put("/user/add", "perms[用户添加]");
        filterChainDefinitionMap.put("/user/edit", "perms[用户编辑]");
        filterChainDefinitionMap.put("/user/del", "perms[用户删除]");
        */

        // 动态授权
        List<Right> rights = rightService.findAll();
        for (Right right : rights) {
            if (right.getRightUrl() != null && !right.getRightUrl().trim().equals("")) {
                filterChainDefinitionMap.put(right.getRightUrl(), "perms[" + right.getRightCode() + "]");
            }
        }

        // 配置认证访问:其他资源(URL)必须认证通过才能访问
        filterChainDefinitionMap.put("/**", "authc"); // 必须放在过滤器链的最后面

        shiroFilterFactory.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactory;
    }

}

 

在测试中如出现了 java.lang.ClassCastException: com.crm.pojo.User cannot be cast to com.crm.pojo.User 异常, 原因是我们的项目可能使用了热部署, 所以造成类加载器不一致所导致。
解决方案:
在 resource 目录下新建 META-INF 目录,在 META-INF 目录下新建 spring-devtools.properties文件,在 spring-devtools.properties 文件中添加以下两条配置信息:
 

		restart.include.shiro-redis=/shiro-[\\w-\\.]+jar
		restart.include.thymeleaf-extras-shiro=/thymeleaf-extras-[\\w-\\.]+jar

配置完成后重启服务就OK了。

加密
哈希与盐
如果你需要保存密码(比如网站用户的密码),你要考虑如何保护这些密码数据,像下面那样直接将密码写入数据库中是极不安全的,因为任何可以打开数据库的人,都将可以直接看到这些密码,比如之前的600w CSDN账号泄露对用户可能造成很大损失。

解决的办法是将密码加密后再存储进数据库,比较常用的加密方法是使用哈希函数(散列算法),常见的散列算法如 MD5、SHA 等。哈希函数的具体定义,大家可以在网上或者相关书籍中查阅到,简单地说,它的特性如下:

原始密码经哈希函数计算后得到一个哈希值
改变原始密码,哈希函数计算出的哈希值也会相应改变
同样的密码,哈希值也是相同的
哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少。

一般进行散列时最好提供一个 salt(盐),加密领域的盐 salt,不是炒菜的调料,而是为了提高加密的安全性。不管是对称加密,还是非对称加密,在用户信息和算法可能被泄漏的情况下,都存在密码被反推算出来的可能。在加密环节如果加盐,等于多了一重安全因素。一般来说,盐就是一个不被外界知道的随机字符串,把用户的明文密码加上盐,再进行加密得到密文(密码的加密后的形式)。

比如加密密码 “admin” ,产生的散列值是 “21232f297a57a5a743894aOe4a801fc3”,可以到一些 MD5 解密网站很容易的通过散列值得到密码 “admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是"密码+用户名+ID",这样生成的散列值相对来说更难破解。
 

    @Test
    public void testMd5Hash() {
        String password = "123456";
        String salt = "czkt";
        long start = System.currentTimeMillis();
        Md5Hash md5Hash = new Md5Hash(password, salt, 1024);
        long end = System.currentTimeMillis();
        System.out.println(md5Hash);
        System.out.println("用时:" + (end - start) + "ms");
        //输出:36aa3e095a86925b7658b5d00557fa77
    }

加密与验证+登录次数限制
加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

如上的 ShiroConfig :


    /**
     * 验证匹配规则
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 使用 md5 算法进行加密
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数:意为加密几次
        hashedCredentialsMatcher.setHashIterations(1024);
        return hashedCredentialsMatcher;
    }

    /**
     * 自定义Realm(基于数据库)
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm shiroRealm = new MyShiroRealm();
        // 设置启用缓存,并设置缓存名称
        shiroRealm.setCachingEnabled(true);
        shiroRealm.setAuthorizationCachingEnabled(true);
        shiroRealm.setAuthorizationCacheName("authorization");
        // 设置凭证(密码)匹配器
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

加 MyShiroRealm.java

package com.bdqn.shiro;

import com.bdqn.pojo.Right;
import com.bdqn.pojo.Role;
import com.bdqn.pojo.User;
import com.bdqn.service.RightService;
import com.bdqn.service.UserService;
import jakarta.annotation.Resource;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.lang.util.ByteSource;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.context.annotation.Lazy;

import java.util.Set;


/**
 * MyShiroRealm(安全数据源)
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Lazy // Shiro框架执行比@Cacheable注解AOP代理早,导致对象代理不成功
    @Resource
    private UserService userService;

    @Lazy // Shiro框架执行比@Cacheable注解AOP代理早,导致对象代理不成功
    @Resource
    private RightService rightService;

    /**
     * 自定义认证流程(机制)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("调用 MyShiroRealm.doGetAuthenticationInfo 获取身份信息!");
        // 获得身份信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userName = token.getUsername();

        // 从数据库中根据用户名查询用户对象(联表查询,包含角色信息)
        User user = userService.getByUsrName(userName);
        if (user == null) {
            throw new UnknownAccountException("用户不存在"); // 用户不存在
        }
        if (user.getUsrFlag() == null || user.getUsrFlag() == 0) {
            throw new LockedAccountException("账号已锁定");
        }
        // 通过用户获取角色信息
        Role role = user.getRole(); // int &207 -> list
        // 根据角色获取权限列表
        Set<Right> rights = rightService.findRightByRoleId(role.getRoleId());
        // 给角色设置权限集合
        role.setRights(rights);
        System.out.println(user);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUsrPassword(), ByteSource.Util.bytes("czkt"), this.getName());
        // 返回身份信息
        return info;
    }

    /**
     * 自定义授权(认证通过才会授权)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("调用 MyShiroRealm.doGetAuthorizationInfo 获取权限信息!");
        // 获得权限信息
        User user = (User) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 静态授权:授予主体(用户)相应的角色和权限
        /*
        info.addRole(user.getRole().getRoleName());
        info.addStringPermission("用户列表"); // 所有用户拥有"用户列表"权限
        if ("管理员".equals(user.getRole().getRoleName())) { // 管理员拥有"增删改"权限
            info.addStringPermission("用户添加");
            info.addStringPermission("用户编辑");
            info.addStringPermission("用户删除");
        }
        */
        // 动态授权:从数据库中获取角色和权限
        Role role = user.getRole();
        if (role != null) {
            info.addRole(role.getRoleName()); // 动态设置角色
            Set<Right> rights = role.getRights();
            if (rights != null && !rights.isEmpty()) {
                for (Right right : rights) {
                    info.addStringPermission(right.getRightCode()); // 动态设置权限
                }
            }
        }
        // 返回授权信息
        return info;
    }

}

控制器:

/**
 * 登录注销相关控制器
 */
@Controller
public class IndexController {

    @Resource
    private UserService userService;

    @Resource
    private RedisService redisService;

    /**
     * 去登录页
     */
    @GetMapping("/login")
    public String toLogin() {
        return "login";
    }

    /**
     * 执行登录操作
     */
    @PostMapping("/login")
    public String doLogin(Model model, HttpSession session, String usrName, String usrPassword) {
        /* User loginUser = userService.login(usrName, usrPassword);
        if (loginUser != null) {
            session.setAttribute("loginUser", loginUser);
            return "redirect:/main"; // 重定向到首页
        } else {
            model.addAttribute("msg", "用户名或密码错误,登录失败! ");
            return "login"; // 必须转发
        } */

        // 使用Shiro安全框架进行登录验证
        try {
            // 构造一个Shiro令牌
            UsernamePasswordToken token = new UsernamePasswordToken(usrName, usrPassword);
            // 创建一个主体
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            // 认证成功
            User loginUser = (User) subject.getPrincipal();
            // 将认证的用户存储到Session
            session.setAttribute("loginUser", loginUser);
            System.out.println(session.toString() + "\nWeb Session: id=" + session.getId() + " usrName=" + ((User) session.getAttribute("loginUser")).getUsrName());
            Session shiroSession = subject.getSession();
            System.out.println(shiroSession.toString() + "\nShiro Session: id=" + shiroSession.getId() + " usrName=" + ((User) shiroSession.getAttribute("loginUser")).getUsrName());
            return "redirect:/main"; // 重定向到首页
        } catch (UnknownAccountException | IncorrectCredentialsException e) {
            model.addAttribute("msg", "用户名或密码错误,登录失败!");
            return "login";
        } catch (LockedAccountException e) {
            model.addAttribute("msg", "用户被禁用,登录失败!");
            return "login";
        } catch (AuthenticationException e) {
            model.addAttribute("msg", "认证异常,登录失败!");
            return "login";
        }
    }

测试时记得启动 Redis

完成

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

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

相关文章

【自动化测试之oracle数据库】MacOs如何安装oracle- client

操作系统为Mac OS&#xff0c;本地在pycharm上跑自动化脚本时&#xff0c;因为有操作oracle数据库的部分&#xff0c;所以需要安装oracle数据库的客户端&#xff0c;并install cx_oracle,本文主要介绍如何在macOS上完成安装&#xff0c;并在python自动化测试代码中配置&#xf…

vue3项目中引入阿里图标库

开篇 本篇的主题是在vue3项目中引入阿里图标库 步骤 注册阿里图标库账号(阿里图标)&#xff0c;并创建项目 将图标加入项目中 将需要的图标先加入购物车&#xff0c;随后加入到项目中 生成项目代码 在项目中生成项目代码&#xff0c;便于后续复制到vue项目中 ## 在vue3项目…

信息安全入门——网络安全威胁

目录 前言网络安全威胁概览悄无声息的数据泄露——被动攻击明目张胆的破坏行为——主动攻击网路世界的瘟疫——病毒总结 前言 在数字化时代&#xff0c;信息安全成为了我们每个人都不得不面对的重要议题。网络安全威胁无处不在&#xff0c;它们可能来自网络的暗角&#xff0c;…

MySQL 9从入门到性能优化-慢查询日志

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

【51单片机】第一个小程序 —— 点亮LED灯

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 单片机介绍LED灯介绍练习创建第一个项目点亮LED灯LED周期闪烁 单片机介绍 单片机&#xff0c;英文Micro Controller Unit&#xff0…

信息安全工程师(68)可信计算技术与应用

前言 可信计算技术是一种计算机安全体系结构&#xff0c;旨在提高计算机系统在面临各种攻击和威胁时的安全性和保密性。 一、可信计算技术的定义与原理 可信计算技术通过包括硬件加密、受限访问以及计算机系统本身的完整性验证等技术手段&#xff0c;确保计算机系统在各种攻击和…

力扣hot100-->递归/回溯

递归/回溯 1. 17. 电话号码的字母组合 中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&…

c语言中整数在内存中的存储

整数的二进制表示有三种&#xff1a;原码&#xff0c;反码&#xff0c;补码 有符号的整数&#xff0c;三种表示方法均有符号位和数值位两部分&#xff0c;符号位都是用‘0’表示“正&#xff0c;用1表示‘负’ 最高位的以为被当作符号位&#xff0c;剩余的都是数值位。 整数…

智慧旅游微信小程序平台

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

Vue3.js - 数据代理方法

1. Vue导入 最简单的方式&#xff0c;通过联网接入Vue3的接口 <script type"text/javascript" src"https://unpkg.com/vue3"></script> 2. Vue实例 2.1 创建Vue实例 const vm Vue.createApp({}) 使用Vue中的createApp方法创建对应实例&a…

115页PPT华为管理变革:制度创新与文化塑造的核心实践

集成供应链&#xff08;ISC&#xff09;体系 集成供应链&#xff08;ISC&#xff09;体系是英文Integrated Supply Chain的缩写&#xff0c;是一种先进的管理思想&#xff0c;它指的是由相互间提供原材料、零部件、产品和服务的供应商、合作商、制造商、分销商、零售商、顾客等…

AI 提示词(Prompt)入门 :ChatGPT 4.0 高级功能指南

这段时间 GPT4 多了很多功能&#xff0c;今天主要是增加了 GPTs Store 的介绍和 创建 GPTs 的简单方法&#xff0c;那么我们开始吧&#xff0c;文末有彩蛋。 这里主要讲解如下几个点&#xff1a; 1&#xff1a; ChatGPT 4.0 插件的使用 2&#xff1a;ChatGPT 4.0 高级数据分…

【小白学机器学习16】 概率论的世界观2: 从正态分布去认识世界

目录 1 从正态分布说起 1.1 正态分布的定义 1.2 正态分布的名字 1.3 正态分布的广泛&#xff0c;和基础性 2 正态分布的公式和图形 2.1 正态分布 2.2 标准正态分布 3 正态分布的认识的3个层次 3.1 第1层次&#xff1a;个体的某个属性的样本值&#xff0c;服从正态分布…

四、大模型(LLMS)langchain面

本文精心汇总了多家顶尖互联网公司在大模型进阶知识考核中的核心考点&#xff0c;并针对这些考点提供了详尽的解答。并提供电子版本&#xff0c;见于文末百度云盘链接中&#xff0c;供读者查阅。 一、大模型langchainmian • 一、什么是 LangChain? • 二、LangChain 包含哪些…

kafka 如何减少数据丢失?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 如何减少数据丢失?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 如何减少数据丢失? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个高吞吐量的分布式消息队列&#xff0c;广泛用…

FPGA第 13 篇,使用 Xilinx Vivado 创建项目,点亮 LED 灯,Vivado 的基本使用(点亮ZYNQ-7010开发板的LED灯)

前言 在FPGA设计中&#xff0c;Xilinx Vivado软件是一款功能强大的设计工具&#xff0c;它不仅支持硬件描述语言&#xff08;HDL&#xff09;的开发&#xff0c;还提供了丰富的图形化设计界面&#xff0c;方便用户进行硬件设计、调试和测试。这里我们将详细介绍&#xff0c;如…

Error in eval(family$initialize): y值必需满足0 <= y <= 1解决

今天在使用R语言对Weekly进行交叉验证时&#xff0c;发生如下报错&#xff1a; 错误于eval(family$initialize): y值必需满足0 < y < 1 错误代码为&#xff1a; Weekly<-read.csv("Weekly.csv") set.seed(1) attach(Weekly) glm.fit1 glm(Direction~Lag…

App测试环境部署

一.JDK安装 参考以下AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 二.SDK安装 安装地址&#xff1a;https://www.androiddevtools.cn/ 解压 环境变量配置 变量名&#xff1a;ANDROID_SDK_HOME 参考步骤&#xff1a; A…

图---java---黑马

图 概念 图是由顶点(vertex)和边(edge)组成的数据结构&#xff0c;例如 该图有四个顶点&#xff1a;A&#xff0c;B&#xff0c;C&#xff0c;D以及四条有向边&#xff0c;有向图中&#xff0c;边是单向的。 有向 vs 无向 如果是无向图&#xff0c;那么边是双向的&#x…

汽车电子工厂中的防静电监控系统,你了解多少?

在汽车电子制造领域&#xff0c;静电放电(ESD)带来的危害不容忽视。微小的静电放电都可能导致电子元器件损坏&#xff0c;进而引发昂贵的返工、产品召回甚至安全事故。因此&#xff0c;有效的防静电监控系统成为汽车电子工厂保障产品质量和安全生产的关键。 传统的防静电措施主…