Shiro框架-史上详解

Shiro框架-史上详解Shiro nbsp 1 权限管理概述 2 Shiro 权限框架 2 1 概念 2 2ApacheShiro 与 SpringSecuri 区别 3 Shiro 认证 3 1 基于 ini 认证 3 2 自定义 Realm 认证 4 Shiro 授权 4 1 基于 ini 授权 4 2 自定义 realm 授权 5 项目集成 shiro 认证 授权注意点 5 1 认证 5 2 授权 5 3 注解 RequiresPerm 5 4 标签式权限验证 6 S

Shiro

2.Shiro权限框架
  2.1 概念
  2.2 Apache Shiro 与Spring Security区别




3.Shiro认证
  3.1 基于ini认证
  3.2 自定义Realm –认证




4.Shiro授权
  4.1 基于ini授权
  4.2 自定义realm – 授权




5.项目集成shiro 认证-授权注意点
  5.1 认证
  5.2 授权
  5.3 注解@RequiresPermissions()
  5.4 标签式权限验证








6.Shiro加密

7.Shiro缓存

权限管理概述

RBAC(Role Based Access Control) :某个用户拥有什么角色,被允许做什么事情(权限)

用户登录—>分配角色—->(权限关联映射)—->鉴权(拥有什么什么权限)

熟悉框架流程

  • 概念 能做什么
  • 架构是怎样
  • 代码怎么实现
  • 项目运用

Shiro权限框架

  • 概念: Apache Shiro 是一个强大且易用的 Java 安全框架
  • 能做什么:Shiro可以帮我们完成 :认证授权、加密、会话管理、与 Web 集成、缓存等。
  • 架构是怎样的

在这里插入图片描述

主要认识:

  • Subject(用户):当前的操作用户 获取当前用户Subject currentUser = SecurityUtils.getSubject()
  • SecurityManager(安全管理器):Shiro的核心,负责与其他组件进行交互,实现 subject 委托的各种功能
  • Realms(数据源) :Realm会查找相关数据源,充当与安全管理间的桥梁,经过Realm找到数据源进行认证,授权等操作
  • Authenticator(认证器): 用于认证,从 Realm 数据源取得数据之后进行执行认证流程处理。
  • Authorizer(授权器):用户访问控制授权,决定用户是否拥有执行指定操作的权限。
  • SessionManager (会话管理器):支持会话管理
  • CacheManager (缓存管理器):用于缓存认证授权信息
  • Cryptography(加密组件):提供了加密解密的工具包


Apache Shiro 与Spring Security区别

Shiro::用于中小型项目比较常见,简单易上手,可以支持多种环境 Shiro 可以不跟任何的框架或者容器绑定,可独立运行 Spring Security:一般多用于spring环境,中大型项目,更强大 Spring Security 则必须要有Spring环境 

Shiro认证

基于ini认证

1.新建项目 --导入依赖 --新建 配置文件ini 2. shiro帮我们创建用户 创建令牌 ,类比页面传入的账号密码 密码错误-- IncorrectCredentialsException 账号错误-- UnknownAccountException 源码分析-- 

在这里插入图片描述

  • 导入依赖
<dependency> <groupId>commons-logging 
     groupId> <artifactId>commons-logging 
      artifactId> <version>1.1.3 
       version>  
        dependency> <dependency> <groupId>org.apache.shiro 
         groupId> <artifactId>shiro-core 
          artifactId> <version>1.5.2 
           version>  
            dependency> <dependency> <groupId>junit 
             groupId> <artifactId>junit 
              artifactId> <version>4.13.2 
               version>  
                dependency> <dependency> <groupId>org.projectlombok 
                 groupId> <artifactId>lombok 
                  artifactId> <version>1.16.22 
                   version> <scope>provided 
                    scope>  
                     dependency> 
  • 编写ini,shiro默认支持的是ini配置的方式(只是演示) shiro-au.ini,真实项目使用xml
#用户的身份、凭据 [users] zhangsan=555 xiaoluo=666 
  • 使用 Shiro 相关的 API 完成身份认证
@Test public void testLogin(){ 
    //创建Shiro的安全管理器,是shiro的核心 DefaultSecurityManager securityManager = new DefaultSecurityManager(); //加载shiro.ini配置,得到配置中的用户信息(账号+密码) IniRealm iniRealm = new IniRealm("classpath:shiro-au.ini"); securityManager.setRealm(iniRealm); //把安全管理器注入到当前的环境中 SecurityUtils.setSecurityManager(securityManager); //无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断 Subject subject = SecurityUtils.getSubject(); System.out.println("认证状态:"+subject.isAuthenticated()); //创建令牌(携带登录用户的账号和密码) UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","666"); //执行登录操作(将用户的和 ini 配置中的账号密码做匹配) subject.login(token); System.out.println("认证状态:"+subject.isAuthenticated()); //登出 //subject.logout(); //System.out.println("认证状态:"+subject.isAuthenticated()); } 

自定义Realm–认证

模拟

@Getter @Setter public class Employee { 
    private String username; private String password;} 

思路:

//继承 AuthorizingRealm //参数的token ,subject进行登录匹配传入的token : UsernamePasswordToken 1.获取用户名 方式一:将token强转为UsernamePasswordToken-->getUsername() 方式二:通过token.getPrincipal() 2.以用户名为条件,查询mysql数据库,得到用户对象(用户名/密码) 模拟从数据库查询的对象 ------->自己new一个 3.将用户对象封装成认证info对象返回 //存在两种可能 1.用户对象为null-----> return null 2.不为空 ---->封装数据 return new SimpleAuthenticationInfo(3个参数 1.employee , //从数据库查询出来需要进行密码匹配的用户对象 2.employee.getPassword(), //匹配对象密码 3.super.getName() //指定realm的名称 ,可自定义 ) 

实现:

public class Realm extends AuthorizingRealm { 
    //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 
    //1.获取登录用户名 String username = (String) token.getPrincipal(); //2.以用户名为条件查询mysql数据库,得到用户对象 //模拟数据 Employee employee = new Employee(); employee.setUsername("xiaoluo"); employee.setPassword("123"); //封装成一个认证info对象 //判断用户是否为空 if (employee!=null){ 
    return new SimpleAuthenticationInfo( employee, employee.getPassword(), super.getName() ); } return null; } } 
@Test public void testLogin(){ 
    //创建Shiro的安全管理器,是shiro的核心 DefaultSecurityManager securityManager = new DefaultSecurityManager(); //自定义Realm,查出用户信息 Realm realm = new Realm(); securityManager.setRealm(realm); //把安全管理器注入到当前的环境中 SecurityUtils.setSecurityManager(securityManager); //无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断 Subject subject = SecurityUtils.getSubject(); System.out.println("认证状态:"+subject.isAuthenticated()); //创建令牌(携带登录用户的账号和密码) UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","123"); //执行登录操作(将用户的和 ini 配置中的账号密码做匹配) subject.login(token); System.out.println("认证状态:"+subject.isAuthenticated()); //登出 //subject.logout(); //System.out.println("认证状态:"+subject.isAuthenticated()); } 

Shiro授权

基于ini授权

  • 登录
  • 分配
  • 鉴权
 权限表达式 资源:操作 admin=*:* //超级管理员 emp=employee:* 
//判断是否有某个角色 subject.hasRole("role"); //用户拥有所有指定角色返回true subject.hasAllRoles(Arrays.aList("role1","role2")); //判断用户是否有指定角色,将结果返回,封装到boolean数组中 boolean[] booleans=subject.hasRoles(Arrays.aList("role1","role2")); //check开头的是没有返回值,当没有权限时就会抛出异常 subject.checkRole("role"); //判断用户是否有某个权限 subject.isPermitted("权限表达式") boolean[] booleans=subject.isPermitted("权限表达式1","权限表达式2") 

在这里插入图片描述

自定义realm — 授权

思路:

1.获取当前登录用户的id/name //获取当前登录用户对象 ,登录传过来的第一个参数 方式一 :principals.getPrimaryPrincipal(); 方式二:SecurityUtils.getSubject().getPrincipal(); 2.以用户id作为条件查询mysql数据,查询该用户拥有角色/权限 //假装查数据 //List 
   
     roles= roleService.queryByEmployee(Employee.getId()); 
    //List 
   
     //List 
    
      roles= permissionService.queryByEmployee(Employee.getId()); 
     
    = permissionService.queryByEmployee(Employee.getId()); 3.将角色与权限封装到授权info对象中 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(roles) //添加角色 info.addStringPermission(permission);//添加权限  

实现:

 //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 
    //获取当前用户对象 Employee employee = (Employee)principalCollection.getPrimaryPrincipal(); //以用户id查询数据库,判断该角色/用户是否拥有权限 模拟 List<String> roles= Arrays.asList("seller"); List<String> permissions= Arrays.asList("customer:list","customer:save"); //封装到info对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(roles); info.addStringPermissions(permissions); return info; } 
public class ShiroDemo { 
    @Test public void testLogin(){ 
    //创建Shiro的安全管理器,是shiro的核心 DefaultSecurityManager securityManager = new DefaultSecurityManager(); //加载shiro.ini配置,得到配置中的用户信息(账号+密码) IniRealm iniRealm = new IniRealm("classpath:shiro-author.ini"); //自定义Realm,查出用户信息 Realm realm = new Realm(); securityManager.setRealm(realm); //把安全管理器注入到当前的环境中 SecurityUtils.setSecurityManager(securityManager); //无论有无登录都可以获取到subject主体对象,但是判断登录状态需要利用里面的属性来判断 Subject subject = SecurityUtils.getSubject(); System.out.println("认证状态:"+subject.isAuthenticated()); //创建令牌(携带登录用户的账号和密码) UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","12"); //执行登录操作(将用户的和 ini 配置中的账号密码做匹配) subject.login(token); System.out.println("认证状态:"+subject.isAuthenticated()); //登出 //subject.logout(); //System.out.println("认证状态:"+subject.isAuthenticated()); //判断用户是否有某个角色 System.out.println("hr:"+subject.hasRole("hr")); System.out.println("seller:"+subject.hasRole("seller")); //是否同时拥有多个角色 System.out.println("是否同时拥有role1和role2:"+subject.hasAllRoles(Arrays.asList("hr", "seller"))); boolean[] booleans = subject.hasRoles(Arrays.asList("hr", "seller")); //System.out.println(booleans); //check开头的是没有返回值的,当没有权限时就会抛出异常 subject.checkRole("seller"); //判断用户是否有某个权限 System.out.println("user:delete:"+subject.isPermitted("user:delete")); subject.checkPermission("customer:list"); } } 
 //如果用户是超级管理员  //设置超级管理员角色 info.addRole("admin") //设置超级管理员权限 info.addStringPermission("*:*"); 

项目集成shiro 认证-授权注意点

认证

1.添加对应依赖 2.配置代理过滤器 为什么不用配置拦截器? Shiro是选择使用filter过滤器来进行拦截的,因为Shiro不依赖Spring容器,所以当没有springmvc时意味着不能用拦截器,但过滤器则不同,只要是web项目都可以使用 3.创建shiro.xml 4.配置shiro过滤器  
     
     
    5.配置安全管理器 DefaultWebSecurityManager 6.修改LoginController 7.配置自定义Realm 8.将自定义Realm交给容器管理 

shiro中的过滤器

过滤器的名称Java
anonorg.apache.shiro.web. lter.authc.AnonymousFilter
authcorg.apache.shiro.web. lter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web. lter.authc.BasicHttpAuthenticationFilter
rolesorg.apache.shiro.web. lter.authz.RolesAuthorizationFilter
permsorg.apache.shiro.web. lter.authz.PermissionsAuthorizationFilter
userorg.apache.shiro.web. lter.authc.UserFilter
logoutorg.apache.shiro.web. lter.authc.LogoutFilter
portorg.apache.shiro.web. lter.authz.PortFilter
restorg.apache.shiro.web. lter.authz.HttpMethodPermissionFilter
sslorg.apache.shiro.web. lter.authz.SslFilter
  • anon: 匿名处理过滤器,即不需要登录即可访问;一般用于静态资源过滤;/static/=anon
  • authc: 表示需要认证(登录)才能使用;(放最后) /=authc
  • logout: 注销过滤器 /logout=logout
  • roles: 角色授权过滤器,验证用户是否拥有资源角色; /employee/input=perms["user:update"]

授权

  • 没有权限的异常 :org.apache.shiro.authz.UnauthorizedException
shiro注解鉴权操作方式: 类比RBAC: 1.自定义权限注解 2.将注解贴在请求映射方法上面 3.将注解标注的权限表达式加载到数据库中 4.将这些表达式根据用户角色进行权限分配 5.当用户登录之后,访问某个请求映射方法时,先经过权限拦截器,进行鉴权操作 1.获取当前登录用户权限表达式集合 2.获取当前请求映射方法头顶上权限表达式 3.判断用户权限表达式集合中是否包含该表达式 

Shiro 权限验证三种方式

  • 编程式
  • 注解式
  • 页面标签式
1.编程式 Subject subject = SecurityUtils.getSubject(); if(subject.hasRole("hr")) { 
    //有权限 } else { 
    //无权限 } -------------------------------------- 2.注解式 @RequiresRoles("admin") @RequiresPermissions("user:create") public void hello() { 
    //有权限 } ---------------------------------------- 3.页面标签式 <@shiro.hasPermission name="employee:list"> <!-- 有权限 --> </@shiro.hasRole> 

授权步骤

1.贴注解 2.开启 Shiro 注解扫描器 3.查询数据库真实数据 自定义realm 

注解@RequiresPermissions()
  • value属性: 这个属性是一个数组
  • Logical.AND: 必须同时拥有value配置所有权限才允许访问
  • Logical.OR:只需要拥有value配置所有权限中一个即可允许访问
约定:权限表达式,第一值权限表达式,第二值为权限名称 @RequiresPermissions(value={ 
   "employee:list","员工列表"},logical=Logical.OR) 逻辑为OR,value满足其中一个条件成立 

标签式权限验证


拓展FreeMarker标签

默认的freemarker是不支持shiro标签,所以需要做功能拓展 ---------------- public class ShiroFreeMarkerConfig extends FreeMarkerConfigurer { 
    @Override public void afterPropertiesSet() throws IOException, TemplateException { 
    //继承之前的属性配置,这不能省 super.afterPropertiesSet(); Configuration cfg = this.getConfiguration(); cfg.setSharedVariable("shiro", new ShiroTags());//注册shiro 标签 } } --------------------------让之前的配置文件 中的FreeMarkerConfigurer修改为自定义的ShiroFreeMarkerConfig<!-- 注册 FreeMarker 配置类 --> <bean class="cn.k.shiro.ShiroFreeMarkerConfig"> <!-- 配置 FreeMarker 的文件编码 --> <property name="defaultEncoding" value="UTF-8" /> <!-- 配置 FreeMarker 寻找模板的路径 --> <property name="templateLoaderPath" value="/WEB-INF/views/" /> <property name="freemarkerSettings"> <props> <!-- 兼容模式 ,配了后不需要另外处理空值问题,时间格式除外 --> <prop key="classic_compatible">true</prop> </props> </property> </bean> 
使用shiro标签
  • authenticated 标签:已认证通过的用户。
<@shiro.authenticated>  
     @shiro.authenticated> 
  • notAuthenticated 标签:未认证通过的用户。
<@shiro.notAuthenticated> 
     @shiro.notAuthenticated> 
  • principal 标签 :输出当前用户信息,通常为登录帐号信息
<@shiro.principal property="name" /> //对应name属性 
  • hasRole 标签:验证当前用户是否拥有该角色
<@shiro.hasRole name="admin">我是管理员 
     @shiro.hasRole> 
  • hasAnyRoles 标签:验证当前用户是否拥有这些角色中的任何一个,角色之间逗号分隔
<@shiro.hasAnyRoles name="admin,user,hr">Hello admin 
     @shiro.hasAnyRoles> 
  • hasPermission 标签:验证当前用户是否拥有该权限
<@shiro.hasPermission name="department:delete">删除 
     @shiro.hasPermission> 

Shiro加密

MD5

//参数1 :原文,参数2:盐 ,参数3:散列次数(加密次数) Md5Hash hash=new Md5Hash("1","kent",3); 

盐一般要求是固定长度的字符串,且每个用户的盐不同。

一般盐的选择的是用户的唯一数据(账号名等),盐是要求不能改变的,不然下次加密结果就对应不上了 

Shiro缓存

当我们登录时,授权信息是要从数据库中查询的,如果每次刷新刷新都需要获取你到底有没有权限,对性能影响不好,用户登录后,授权信息一般很少改动,所以,我们可以将第一次授权后,将信息存在缓存中,下次直接再缓存中获取,就很好的避免了多次访问数据库

Shiro没有实现自己的缓存机制,只提供了支持缓存的API接口,这里使用的是EhCache

mybatis 一级 二级缓存------百度(ing)补充 


集成EhCache

第三方EhCache 1.配置缓存管理器并引用缓存管理器 <!--安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--注册自定义数据源--> <property name="realm" ref="employeeRealm"/> <!--注册缓存管理器--> <property name="cacheManager" ref="cacheManager"/> </bean> <!-- 缓存管理器 --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <!-- 设置配置文件 --> <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/> </bean> ---------------------------------------- 2.添加缓存配置文件 shiro-ehcache.xml <ehcache> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache> 
配置缓存文件属性说明
  • maxElementsInMemory: 缓存对象最大个数。
  • eternal:对象是否永久有效 ,一般为false
  • timeToIdleSeconds: 对象空闲时间
  • timeToLiveSeconds:对象存活时间
  • memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。
  • LFU(较少使用,意思是一直以来最少被使用的,缓存的元素有一个hit 属性(命中率),hit 值最小的将会被清出缓存)默认

拓展 统一全局异常

@ControllerAdvice 控制器功能增强注解

1.在进入请求映射方法之前做功能增强,经典用法:date日期格式化 2.在进入请求映射方法之后做功能增强,经典用法:统一异常处理 3.处理异常的方法,方法需要贴ExceptionHandler注解 


配置文件

配置代理过滤器

<filter> <filter-name>shiroFilter 
     filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy  
      filter-class>  
       filter> <filter-mapping> <filter-name>shiroFilter 
        filter-name> <url-pattern>/* 
         url-pattern>  
          filter-mapping> 

配置shiro.xml

 
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">  
    <bean id="employeeRealm" class="cn.k.shiro.EmployeeRealm">  
     bean>  
     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="employeeRealm"/>  
     <property name="cacheManager" ref="cacheManager"/>  
      bean>  
      <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
      <property name="securityManager" ref="securityManager"/>  
      <property name="loginUrl" value="/login.html"/>  
      <property name="filterChainDefinitions"> <value> /userLogin=anon /css/=anon /js/=anon /img/=anon /upload/=anon /userLogout=logout /=authc  
       value>  
        property>  
         bean>  
          
         <aop:config/>  
         <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/>  
          bean>  
          <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">  
          <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>  
           bean>  
            beans> 

关联mvc.xml

<import resource="classpath:shiro.xml"/> 

loginController

@RequestMapping("/userLogin") @ResponseBody public JsonResult login(String username, String password){ 
    try { 
    UsernamePasswordToken token = new UsernamePasswordToken(username, password); SecurityUtils.getSubject().login(token); return new JsonResult(); } catch (UnknownAccountException e) { 
    return new JsonResult(false, "账号不存在"); } catch (IncorrectCredentialsException e) { 
    return new JsonResult(false, "密码错误"); } catch (Exception e) { 
    e.printStackTrace(); return new JsonResult(false, "登录异常,请联系管理员"); } } 

关联mvc.xml

<import resource="classpath:shiro.xml"/> 

loginController

@RequestMapping("/userLogin") @ResponseBody public JsonResult login(String username, String password){ 
    try { 
    UsernamePasswordToken token = new UsernamePasswordToken(username, password); SecurityUtils.getSubject().login(token); return new JsonResult(); } catch (UnknownAccountException e) { 
    return new JsonResult(false, "账号不存在"); } catch (IncorrectCredentialsException e) { 
    return new JsonResult(false, "密码错误"); } catch (Exception e) { 
    e.printStackTrace(); return new JsonResult(false, "登录异常,请联系管理员"); } } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/198578.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午2:53
下一篇 2026年3月26日 下午2:53


相关推荐

  • PyCharm批量注释和批量缩进快捷键

    PyCharm批量注释和批量缩进快捷键1 批量注释 选择要注释的文本行 背景变化后 同时按 Ctrl 2 取消批量注释 选择已注释的文本行 背景变化后 同时按 Ctrl 3 批量缩进 选择要缩进的文本行 背景变化后 按下 TAB 键 4 取消批量缩进 选择要缩进的文本行 背景变化后 按下 SHIFT TAB 键

    2026年3月26日
    2
  • jquery setvalue_jquery on方法

    jquery setvalue_jquery on方法setInterval()方法<!DOCTYPEhtml><html><headlang=”en”><metacharset=”UTF-8″><title></title><scriptsrc=”jquery.js”></script><st…

    2025年10月1日
    7
  • 黄仁勋推出英伟达版“小龙虾” NemoClaw:主打“一键安装”

    黄仁勋推出英伟达版“小龙虾” NemoClaw:主打“一键安装”

    2026年3月18日
    1
  • 写给三维建模入门小白的建议

    写给三维建模入门小白的建议各位三维建模入门的小白们 下面这些建议一定可以在三维建模的学习过程中有所帮助 强烈建议大家收藏哦 第一 要想学会操作 3DMAX 就必须要熟练地使用电脑 鼠标和键盘的灵活度是必须要的 因为在接触 3DMAX 后 很多指令都需要用到快捷键 我们在后面会讲到 3DMAX 快捷键以及它的重要性的 所以对电脑越是熟悉 学起来就越简单顺利 第一步要学会安装 3DMAX 软件 了解 3DMAX 的操作界面 3DMAX 的基本知识一定要先掌握 比如 3DMAX 的界面与概述 文件与视图菜单在什么位置 主工具栏中都是什么工具 三维模型的创建与编

    2025年8月15日
    6
  • phpexcel中文手册

    phpexcel中文手册首先到 phpexcel 官网上下载最新的 phpexcel 类 下周解压缩一个 classes 文件夹 里面包含了 PHPExcel php 和 PHPExcel 的文件夹 这个类文件和文件夹是我们需要的 把 classes 解压到你项目的一个目录中 重名名为 phpexcel 开始喽 代码都摘自自带实例 程序部分 require once phpexcel PHPExcel php 首先创建一个新的对象 PHPExcelobje objPHPExcel newPHP

    2026年3月18日
    2
  • vscode怎么直接运行html_vscode怎么创建项目

    vscode怎么直接运行html_vscode怎么创建项目首先新建一个html文件,取名后缀为XXX.html,如图建好文件之后是空的,然后输入!同时按下tab键就自动生成html目录了目录建好后怎么运行呢?打开扩展商店,输出op就出来了,选择第一个openinbrowser安装安装好了之后就可以运行啦,快捷键是alt+b,当然也可以在项html文件中点击鼠标右键来运行,如图然后选择浏览器来运行,推荐最好选择谷歌,运行效果如下最后有人问,这个中文是怎么设置的?也是在应用商店里面搜索language然后选择中文的下载然后重新启动VSCo

    2022年8月22日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号