SpringBoot--中间件技术-4:整合Shiro,Shiro基于会话SessionManager实现分布式认证,附案例含源代码!

SpringBoot整合安全中间件Shiro

技术栈:SpringBoot+Shiro

代码实现

  1. pom文件加坐标

    Springboot版本选择2.7.14 ;java版本1.8 ; shiro做了版本锁定 1.3.2

    <properties><java.version>1.8</java.version><!--shiro版本锁定--><shiro.version>1.3.2</shiro.version>
    </properties>
    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.21</version></dependency><!--mp--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!-- SECURITY begin --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency><!-- SECURITY end -->
    </dependencies>
    
  2. 主配置文件

    #配置数据源
    spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/spring?serverTimezone=GMTusername: rootpassword: 123456#配置自动驼峰映射
    mybatis:configuration:map-underscore-to-camel-case: truetype-aliases-package: com.dong.pojo
    #MP配置自动驼峰映射
    mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis所执行的sql输出控制台
    
  3. POJO实体类

    Permission

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @Component
    @TableName(value = "pe_permission")
    public class Permission {@TableField(value = "id")private String id;@TableField(value = "name")private String name;@TableField(value = "code")private String code;@TableField(value = "description")private String description;
    }
    

    Role

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @TableName(value = "pe_role")
    @Component
    public class Role {@TableField(value = "id")private String id;@TableField(value = "name")private String name;@TableField(value = "code")private String code;@TableField(value = "description")private String description;// 外部属性@TableField(exist = false)private List<Permission> permissions;
    }
    

    Users

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @TableName(value = "pe_user")
    @Component
    public class Users {@TableId(value = "id")private String id;@TableField(value = "username")private String username;@TableField(value = "password")private String password;@TableField(value = "salt")private String salt;// 外部属性@TableField(exist = false)private List<Role> rolesList;public Users(String id, String username, String password) {this.id = id;this.username = username;this.password = password;}
    }
    
  4. dao层

    UsersMapper

    @Mapper
    public interface UserMapper extends BaseMapper<Users> {@Insert("insert into pe_user(id,username,password,salt) values(#{id},#{username},#{password},#{salt})")public int save(Users users);// 级联查询@Results(id = "users",value = {@Result(column = "id",property = "rolesList",many = @Many(select = "com.dong.springboot_mp_shiro.com.dong.mapper.RoleMapper.findById"))})@Select("select * from pe_user where username=#{v}")public Users findUserDetail(String name);// 简单查询@Select("select * from pe_user where username=#{v}")public Users findBaseUser(String name);
    }
    

    RoleMapper

    @Mapper
    public interface RoleMapper extends BaseMapper<Role> {@Results(id = "role",value = {@Result(column = "id",property = "permissions",many=@Many(select = "com.dong.springboot_mp_shiro.com.dong.mapper.PermissionMapper.findByPermissionId"))})@Select("select * from pe_role where id in (select role_id from pe_user_role where user_id =#{v} )")public Role findById(String id);}
    

    PermissionMapper

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.dong.springboot_mp_shiro.com.dong.pojo.Permission;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    @Mapper
    public interface PermissionMapper extends BaseMapper<Permission> {@Select("select * from pe_permission where id in (select permission_id from pe_role_permission where role_id =#{v})")public Permission findByPermissionId(String permissionId);
    }
    
  5. service层

    接口:

    IUserService

    public interface IUserService {public int save(Users users);public Users baseFindUser(String name);public Users findUserDetail(String Name);
    }
    

    IRoleService

    public interface IRoleService {
    }
    

    IPermissionService

    public interface IPermissionService {
    }
    

    实现类:

    UserServiceImp

    @Service
    public class UserServiceImp implements IUserService {@Autowired(required = false)private UserMapper uMapper;@Overridepublic int save(Users users) {System.out.println("service:"+ users);// 获取salt字符串String salt = DigestsUtil.generateSalt();// 密码加密String password = DigestsUtil.generatePassword(users.getPassword(), salt);users.setPassword(password);users.setSalt(salt);int res = uMapper.save(users);return res;}@Overridepublic Users baseFindUser(String name) {Users baseUser = uMapper.findBaseUser(name);return baseUser;}@Overridepublic Users findUserDetail(String name) {Users userDetail = uMapper.findUserDetail(name);return userDetail;}
    }
    
  6. controller层

    @RestController
    public class UserController {@Autowired(required = false)private UserServiceImp service;// 首页@RequiresPermissions("user-home")@RequestMapping("/user/home")public  String home(){return "访问个人主页成功";}// 用户注册@RequiresPermissions("user-add")@RequestMapping("/user/{id}")public String save(@PathVariable String id){/*int res = service.save(users);if(res>0){return "添加成功";}else{return "添加失败";}*/return "新增成功";}@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)public String delete(@PathVariable String id){return "删除成功";}@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}",method = RequestMethod.PUT)public String update(@PathVariable String id){return "修改成功";}@RequiresPermissions("user-find")@RequestMapping(value = "/user",method = RequestMethod.GET)public String find(){return "查询成功";}// 登录认证@RequestMapping("/login")public String login(Users users){try {// 构造登录令牌UsernamePasswordToken token = new UsernamePasswordToken(users.getUsername(), users.getPassword());// 获取subjectSubject subject = SecurityUtils.getSubject();// 调用subject认证subject.login(token);return "登录成功";} catch (AuthenticationException e) {return  "用户名或密码错误";}}// 未登录跳转@RequestMapping("/autherror")public String autherror(){return "未认证,请登录";}
    }
    

    ==@RequiresPermissions(" "):==标注访问该资源需要的权限

    • 执行subject.long登录方法,执行Reaml的AuthenticationException方法

    • 鉴权授权,执行Reaml的AuthorizationInfo方法

  7. MyRealm

    public class MyRealm extends AuthorizingRealm {@Autowired(required = false)private UserServiceImp serviceImp;// 授权鉴权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {// 获取已经认证的用户数据Users users = (Users) principalCollection.getPrimaryPrincipal();// 查询用户的详细信息Users userDetail = serviceImp.findUserDetail(users.getUsername());HashSet<String> perms = new HashSet<>(); // 权限set集合HashSet<String> roles = new HashSet<>(); // 角色set集合for(Role role: userDetail.getRolesList() ){roles.add(role.getCode());for(Permission permission: role.getPermissions() ){perms.add(permission.getCode());}}SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(perms);info.setRoles(roles);return info;}// 认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {// 获取用户登陆输入的密码(token)UsernamePasswordToken upToken =  (UsernamePasswordToken)authenticationToken;// 用户输入的账号String username = upToken.getUsername();Users users = serviceImp.baseFindUser(username);if(users != null){SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(users, users.getPassword(), ByteSource.Util.bytes(users.getSalt()), "MyRealm");return info;}// 账号查不到,返回null(抛出异常)return null;}@PostConstruct   // 属性初始化public void  initCredentialsMatcher(){// 指定密码算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);// 指定迭代次数hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);// 生成密码比较器setCredentialsMatcher(hashedCredentialsMatcher);}
    }
    

    @PostConstruct注解,属性初始化

    加密工具类

    public class DigestsUtil {// 编码方式public static final String SHA1="SHA-1";// 加密次数public static final Integer COUNTS=369;//  获取salt字符串public static String generateSalt(){SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();return secureRandomNumberGenerator.nextBytes().toHex();}// 生成密文密码public static String generatePassword(String input,String salt){return new SimpleHash(SHA1,input,salt,COUNTS).toString();}}
    
  8. Shiro配置类:ShiroConfiguration

    @Configuration
    public class ShiroConfiguration {/*** 1.创建shiro自带cookie对象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.创建realm@Beanpublic MyRealm getRealm() {return new MyRealm();    //new 自定义的Reaml}/*** 3.创建会话管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.创建安全管理器@Beanpublic SecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保证实现了Shiro内部lifecycle函数的bean执行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.开启对shiro注解的支持*   AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}/** 8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制* */@Beanpublic ShiroFilterFactoryBean shiroFilter(){//  1.创建过滤工厂ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();// 2.设置安全管理器filterFactoryBean.setSecurityManager(defaultWebSecurityManager());// 3.通用配置(跳转登录页面,为授权跳转的页面)filterFactoryBean.setLoginUrl("/autherror");//4.设置过滤器集合//key = 拦截的url地址//value = 过滤器类型LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/login","anon");//当前请求地址可以匿名访问filterMap.put("/user/**","authc");// 当前请求地址必须认证之后才可以访问// 在过滤器工程内设置系统过滤器filterFactoryBean.setFilterChainDefinitionMap(filterMap);return filterFactoryBean;}
    }
    
  9. 统一异常处理器

    @ControllerAdvice
    public class UserControllerAdv {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String tongyi(HttpServletRequest request , HttpServletResponse response, AuthorizationException e){return "未授权---统一异常处理器";}}
    

Shiro实现分布式会话SessionManager

代码结构:代码结构和SpringBoot整合Shiro中的案例相同

问题:如图

在这里插入图片描述

解决思路:将当前的session会话存到缓存Redis中

在这里插入图片描述

实现步骤:

  1. 创建RedisSessionDao extends AbstractSessionDAO
  2. 配置ShiroConfig

代码实现:

  1. RedisSessionDao

    public class RedisSessionDao extends AbstractSessionDAO {@Autowiredprivate RedisTemplate redisTemplate;//创建会话@Overrideprotected Serializable doCreate(Session session) {Serializable sessionId = generateSessionId(session);assignSessionId(session, sessionId);redisTemplate.opsForValue().set(sessionId,session);return sessionId;}@Overrideprotected Session doReadSession(Serializable sessionId) {return (Session) redisTemplate.opsForValue().get(sessionId);}@Overridepublic void delete(Session session) {redisTemplate.delete(session.getId());}@Overridepublic Collection<Session> getActiveSessions() {return Collections.emptySet();}@Overridepublic void update(Session session) {redisTemplate.opsForValue().set(session.getId(),session);}}
    
  2. 需要操作Redis,整合Redis

    1. 导入redis坐标

      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
    2. yaml主配置文件配置redis

      server:port: 8080
      #配置数据源
      spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/spring?serverTimezone=GMTusername: rootpassword: 123456redis:port: 6379#配置自动驼峰映射
      mybatis:configuration:map-underscore-to-camel-case: truetype-aliases-package: com.dong.pojo
      #MP配置自动驼峰映射
      mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl #mybatis所执行的sql输出控制台
      
    3. ShiroConfig

      向容器中注入一个SessionDao,把SessionDao绑定给会话管理器

      @Configuration
      public class ShiroConfiguration {/*** 1.创建shiro自带cookie对象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.创建realm@Beanpublic MyRealm getRealm() {return new MyRealm();}// 向容器中注入一个SessionDao@Beanpublic SessionDAO redisSessionDao(){RedisSessionDao sessionDAO =   new RedisSessionDao();return sessionDAO;}/*** 3.创建会话管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();//把SessionDao绑定给会话管理器sessionManager.setSessionDAO(redisSessionDao()); sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.创建安全管理器@Beanpublic SecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保证实现了Shiro内部lifecycle函数的bean执行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.开启对shiro注解的支持*   AOP式方法级权限检查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}/** 8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制* */@Beanpublic ShiroFilterFactoryBean shiroFilter(){//  1.创建过滤工厂ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();// 2.设置安全管理器filterFactoryBean.setSecurityManager(defaultWebSecurityManager());// 3.通用配置(跳转登录页面,为授权跳转的页面)filterFactoryBean.setLoginUrl("/autherror");//4.设置过滤器集合//key = 拦截的url地址//value = 过滤器类型LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/login","anon");//当前请求地址可以匿名访问filterMap.put("/user/**","authc");filterFactoryBean.setFilterChainDefinitionMap(filterMap);return filterFactoryBean;}
      }
      

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一起学docker系列之四docker的常用命令--系统操作docker命令及镜像命令

目录 前言1 操作 Docker 的命令1.1 启动 Docker1.2 停止 Docker1.3 重启 Docker1.4 查看 Docker 状态1.5 查看 Docker 所有命令的信息1.6 查看某个命令的帮助信息 2 操作镜像的命令2.1 查看所有镜像2.2 搜索某个镜像2.3 下载某个镜像2.4 查看镜像所占空间2.5 删除镜像2.6 强制删…

nodejs+vue杰和牧场管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

系统涉及的对象是奶牛。 系统使用员工有管理员和普通员工。 管理员有修改的权限&#xff0c;普通员工没有。系统包含新闻功能&#xff0c;最好是有个后台管理&#xff0c;在后台输入新闻标题和内容&#xff0c;插入图片&#xff0c;在网页上就可以展示。最好再有个轮播图。 新闻…

SpringCloud Alibaba组件入门全方面汇总(中):服务熔断降级-Sentinel

文章目录 Sentinel常见的容错思路Sentinel流量控制规则sentinel 自定义异常 sentinelresources 注解使用Feign整合Sentinel**面试题&#xff1a;结合Feign后&#xff0c;你在项目中的降级方法中会实现什么样的操作/功能&#xff1f;** Sentinel Sentinel是阿里巴巴开源的分布…

The import xxx.xxx.xxxx is never used

CTRL SHIFT O 就完成了&#xff0c;懒人&#xff0c;代码没洁癖啊&#xff0c;几千上万的代码没用的。

C++语言的由来与发展历程

C语言的由来与发展历程可以追溯到1978年&#xff0c;当时美国电话电报公司&#xff08;AT&T&#xff09;的贝尔实验室发明了C语言&#xff0c;以满足UNIX操作系统的开发需求。在C语言的基础上&#xff0c;Bjarne Stroustrup于1983年创立了C编程语言&#xff0c;作为C语言的…

鸿蒙开发|鸿蒙系统项目开发前的准备工作

文章目录 鸿蒙项目开发的基本流程介绍鸿蒙项目开发和其他项目有什么不同成为华为开发者-注册和实名认证1.登录官方网站 鸿蒙项目开发的基本流程介绍 直接上图&#xff0c;简单易懂&#xff01; 整个项目的开发通过4个模块进行&#xff1a;开发准备、开发应用、运行调试测试和发…

酷柚易汛ERP - 通用设置操作指南

1、系统设置 对系统进行初步设置&#xff0c;如系统LOGO、站点名称、备案号、版权信息、尾部信息及系统相关的一些基础设置 2、应用/小程序配置 对系统移动端进行相关配置 3、短信配置 对系统短信进行配置&#xff0c;此配置用于移动端一些通知类信息发送【目前仅支持阿里云…

OpenCV快速入门:初探

文章目录 一、什么是OpenCV二、安装OpenCV三、图像读取与显示读取图像显示图像等待按键与关闭窗口 四、视频加载与摄像头调用从视频文件中读取从摄像头中读取关闭窗口与释放资源 五、图像的基本存储方式RGB矩阵矩阵操作与像素访问使用矩阵来显示图像 六、图像保存读取图像保存图…

Win10专业版如何重装-Win10专业版重装系统教程

Win10专业版如何重装&#xff1f;Win10专业版系统能够用户带来丰富的功能服务&#xff0c;用户操作需求轻松得到满足。如果我们在Win10专业版电脑中&#xff0c;遇到了系统问题&#xff0c;这时候可以考虑重新安装Win10专业版系统&#xff0c;从而解决系统出现的问题。下面小编…

gpt-4-vision-preview 识图

这些图片都是流行动画角色的插图。 第一张图片中的角色是一块穿着棕色方形裤子、红领带和白色衬衫的海绵&#xff0c;它站立着并露出开心的笑容。该角色在一个蓝色的背景前&#xff0c;显得非常兴奋和活泼。 第二张图片展示的是一只灰色的小老鼠&#xff0c;表情开心&#xf…

【0235】修改私有内存(private memory)中的MyBEEntry时,st_changecount值前后变化

上一篇: 【0234】PgBackendStatus 记录当前postgres进程的活动状态 1. pg_stat_activity中xxx实时信息如何实现? 客户端(eg:psql)在连接上postmaster之后,postmaster守护进程会fork()一个后端进场(backend process),之后此客户端的所有操作、交互均有此对应的Backen…

WPF 使用.ttf文件中的图标失败

本章讲述问题&#xff1a;WPF 使用.ttf文件中的图标失败&#xff0c;变成白框问题。 在WPF开发过程中&#xff0c;我们需要使用.ttf文件中的图标和文字&#xff0c;但是经常会遇到类似问题&#xff1a;WPF 在XMAL里增加图标字体时没办法实时显示出来只显示一个小方框&#xff0…

公寓水电管理系统

springbootmybatisthymeleaf 这次练习是尝试将layer与系统结合起来&#xff0c;将新增、修改、删除都和弹窗结合起来。 一、需求分析 二、数据库 三、模块 1、登录页面 哈哈哈&#xff0c;之前做的登录页面都好丑&#xff0c;这是目前做的最好看的一次了。 超级管理员&…

JS-项目实战-鼠标悬浮变手势(鼠标放单价上生效)

1、鼠标悬浮和离开事件.js //当页面加载完成后执行后面的匿名函数 window.onload function () {//get:获取 Element:元素 By:通过...方式//getElementById()根据id值获取某元素let fruitTbl document.getElementById("fruit_tbl");//table.rows:获取这个表格…

20231117在ubuntu20.04下使用ZIP命令压缩文件夹

20231117在ubuntu20.04下使用ZIP命令压缩文件夹 2023/11/17 17:01 百度搜索&#xff1a;Ubuntu zip 压缩 https://blog.51cto.com/u_64214/7641253 Ubuntu压缩文件夹zip命令 原创 chenglei1208 2023-09-28 17:21:58博主文章分类&#xff1a;LINUX 小工具 文章标签命令行压缩包U…

总结 CNN 模型:将焦点转移到基于注意力的架构

一、说明 在计算机视觉时代&#xff0c;卷积神经网络&#xff08;CNN&#xff09;几十年来一直是主导范式。直到 2021 年 Vision Transformers (ViTs) 出现&#xff0c;这个领域才开始发生变化。现在&#xff0c;是时候采用受 Transformer 架构启发的基于注意力的模型了&#x…

GZ038 物联网应用开发赛题第8套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第8套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

Android Studio Error “Unsupported class file major version 61“---异常信息记录

编译时异常信息 原因及解决办法 问题出在JAVA 17上&#xff0c;并且使用的Gradle JDK是&#xff1a;Android Studio java home版本17.0.1将其更改为&#xff1a;Android Studio默认JDK版本11.0.10 即可解决 操作步骤 1 2 3

uniapp App 端 版本更新检测

function checkVersion() { var req { //升级检测数据 appid: plus.runtime.appid, version: plus.runtime.version }; const timestamp Date.parse(new Date()); config.server.query_news uni.reque…

如何使用ONLYOFFICE来P惊悚特效图

如何使用ONLYOFFICE来P惊悚特效图 老朋友们可能会经常看见本号主又换头像了&#xff0c;各种各样精神分裂成一群人或者我和自己俩个人的头像&#xff0c;之前讲过的&#xff1a; 手把手教你如何自己一个人精神分裂成一群人https://mp.weixin.qq.com/s/yacKt7N3sZnarfMhXRNdBA…