SpringBoot整合Shiro实现登录认证,鉴权授权
文章目录
- 前言
- 一、shiro简介
- 二、环境搭建
- 2.1.数据库
- 2.1.1user用户表
- 2.1.2user_role用户角色关系表
- 2.1.3role角色表
- 2.1.4role_permission角色权限关系表
- 2.1.5permission权限表
- 2.2导坐标
- 2.3实体类
- 2.3.1User
- 2.3.2Role
- 2.3.3Permission
- 2.4MVC三层
- 2.4.1User
- 2.4.1.1mapper层
- 2.4.1.2service层
- 2.4.1.3controller层
- 2.4.2Role
- 2.4.2.1mapper层
- 2.4.2.2service层
- 2.4.2.3controller层
- 2.3.3Permission
- 2.4.3.1mapper层
- 2.4.3.2service层
- 2.4.3.3controller层
- 2.5.加密工具类
- 2.6.全局异常处理器
- 2.7自定义Realm
- 2.8shiro配置类
- 三、案例演示
- 3.1 登录认证
- 3.2 权限认证
- 总结
前言
近期对Springboot框架的学习中,为了更好的学习理解Springsecurity中间件,先学习了一下“老派”的shiro安全框架,本文章将通过注解的方式实现基础的用户认证和角色授权案例
一、shiro简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
二、环境搭建
2.1.数据库
2.1.1user用户表

2.1.2user_role用户角色关系表

2.1.3role角色表

2.1.4role_permission角色权限关系表

2.1.5permission权限表

2.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-web</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><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><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.3实体类
2.3.1User
@TableName("pe_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {@TableId(value = "id",type = IdType.AUTO)private int id;private String username;private String password;private String salt;@TableField(exist = false)private Set<Role> roles;
}
2.3.2Role
@TableName("pe_role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;@TableField(exist = false)private Set<Permission> permissions;
}
2.3.3Permission
@TableName("pe_permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;
}
2.4MVC三层
2.4.1User
2.4.1.1mapper层
public interface UserMapper extends BaseMapper<User> {@Select("select * from pe_user where username = #{name}")User findUserByName(String name);//级联查询@Results({@Result(column = "id",property = "id"),@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(column = "salt",property = "salt"),@Result(column = "id",property = "roles",many=@Many(select = "com.apesource.shirostudy_demo_05.dao.RoleMapper.findRoleIdsByUserId"))})@Select("select * from pe_user where id = #{id}")User findUserDetailById(@Param("id") int id);
}
2.4.1.2service层
public interface IUserService extends IService<User> {User findUserByName(String name);User findUserDetailById(int id);
}@Service
@SuppressWarnings("all")
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements IUserService {@AutowiredUserMapper userMapper;@AutowiredIRoleService roleService;@Overridepublic User findUserByName(String name) {User user = userMapper.findUserByName(name);return user;}@Overridepublic User findUserDetailById(int id) {//根据userid查询userUser user = userMapper.findUserDetailById(id);//返回return user;}
}
2.4.1.3controller层
@RestController
public class UserController {@Autowiredprivate IUserService userService;//个人主页@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "访问个人主页成功";}//根据名字查询用户基本信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userByName/{name}")public String findUserByName(@PathVariable String name) {System.out.println(name);User user = userService.findUserByName(name);return user.toString();}//更新@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)public String update(@PathVariable String id) {return "更新用户成功";}//删除@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)public String delete(@PathVariable String id) {return "删除用户成功";}//根据id查询用户详细信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userById/{id}")public String findUserDetailById(@PathVariable int id) {return userService.findUserDetailById(id).toString();}//登录页面@RequestMapping(value = "/login")public String login(User user) {try {//1.构造登录令牌UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());//2.//2.获取subjectSubject subject = SecurityUtils.getSubject();//3.调用subject进行登录subject.login(token);return "登录成功!!!";} catch (AuthenticationException e) {return "用户名或密码错误!!!";}}//未登陆与未授权页面@RequestMapping(value = "/autherror")public String autherror() {return "未登录";}}
2.4.2Role
2.4.2.1mapper层
public interface RoleMapper extends BaseMapper<Role> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),@Result(column = "id",property = "permissions",many =@Many(select = "com.apesource.shirostudy_demo_05.dao.PermissionMapper.findPermissionIdsByRoleId"))})@Select("SELECT * FROM pe_role WHERE id IN (SELECT role_id FROM pe_user_role WHERE user_id = #{id})")Set<Role> findRoleIdsByUserId(int id);
}
2.4.2.2service层
public interface IRoleService extends IService<Role> {Set<Role> findRoleIdsByUserId(int id);
}
@Service
@SuppressWarnings("all")
public class RoleServiceImp extends ServiceImpl<RoleMapper, Role> implements IRoleService {@AutowiredRoleMapper roleMapper;@AutowiredIPermissionService permissionService;@Overridepublic Set<Role> findRoleIdsByUserId(int id) {Set<Role> roles = roleMapper.findRoleIdsByUserId(id);return roles;}
}
2.4.2.3controller层
由于不演示,无。
2.3.3Permission
2.4.3.1mapper层
public interface PermissionMapper extends BaseMapper<Permission> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),})@Select("SELECT * FROM pe_permission WHERE id IN (SELECT permission_id FROM pe_role_permission WHERE role_id = #{id} ) ")Set<Permission> findPermissionIdsByRoleId(int id);
}
2.4.3.2service层
public interface IPermissionService extends IService<Permission> {Set<Permission> findPermissionIdsByRoleId(int id);
}
@Service
public class PermissionServiceImp extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {@Autowired(required = false)PermissionMapper permissionMapper;@Overridepublic Set<Permission> findPermissionIdsByRoleId(int id) {Set<Permission> permissionIds = permissionMapper.findPermissionIdsByRoleId(id);return permissionIds;}
}
2.4.3.3controller层
由于不演示,无。
2.5.加密工具类
public class DigestsUtil {public static final String SHA1 = "SHA-1";public static final Integer COUNTS =369;/*** @Description sha1方法* @param input 需要散列字符串* @param salt 盐字符串* @return*/public static String show(String input, String salt) {return new SimpleHash(SHA1, input, salt,COUNTS).toString();}/*** @Description 随机获得salt字符串* @return*/public static String generateSalt(){SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();return randomNumberGenerator.nextBytes().toHex();}/*** @Description 生成密码字符密文和salt密文* @param* @return*/public static Map<String,String> entryptPassword(String passwordPlain) {Map<String,String> map = new HashMap<>();String salt = generateSalt();String password =show(passwordPlain,salt);map.put("salt", salt);map.put("password", password);return map;}
}
2.6.全局异常处理器
@ControllerAdvice
public class BaseExceptionHandler {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e){return "未授权!!!";}
}
2.7自定义Realm
public class MyRealm extends AuthorizingRealm {@AutowiredIUserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {int userId = (int) principalCollection.getPrimaryPrincipal();User user = userService.findUserDetailById(userId);Set<String> roles = new HashSet<>();Set<String> permissions = new HashSet<>();user.getRoles().stream().forEach(role -> {roles.add(role.getCode());role.getPermissions().stream().forEach(permission -> {permissions.add(permission.getCode());});});SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addRoles(roles);info.addStringPermissions(permissions);return info;}/*** 认证方法* 参数:传递的用户名密码*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.获取登录的用户名密码(token)UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//2.根据用户名查询数据库//mybatis情景下:user对象中包含ID,name,pwd(匿名)User user = userService.findUserByName(username);if(user != null){SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"myRealm");return info;}return null;}/*** @Description 自定义密码比较器* @param* @return* bean标签 init-method属性*/@PostConstructpublic void initCredentialsMatcher() {//指定密码算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);//指定迭代次数hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);//生效密码比较器setCredentialsMatcher(hashedCredentialsMatcher);}
}
2.8shiro配置类
@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();}/*** 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 filterFactory = new ShiroFilterFactoryBean();//2.设置安全管理器filterFactory.setSecurityManager(defaultWebSecurityManager());//3.通用配置(跳转登录页面,为授权跳转的页面)filterFactory.setLoginUrl("/autherror");//跳转url地址//4.设置过滤器集合//key = 拦截的url地址//value = 过滤器类型Map<String,String> filterMap = new LinkedHashMap<>();//key:请求规则 value:过滤器名称filterMap.put("/login","anon");//当前请求地址可以匿名访问filterMap.put("/user/**","authc");//当前请求地址必须认证之后可以访问//在过滤器工程内设置系统过滤器filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}}
三、案例演示
用户密码均是123456加密后保存至数据库
3.1 登录认证
成功登录:
失败登录:
3.2 权限认证
由于刚刚登录的信息有管理员和员工的身份,所以其所有功能都能使用。
有权限时:
无权限时:
无权限时会被全局异常处理器拦截,并作出响应的响应。
总结
通过对shrio的学习,更好的理解了安全认证以及鉴权授权的流程,大概了解了安全认证的机制,对我之后学习理解Springsecurity有更好的帮助。
相关文章:
SpringBoot整合Shiro实现登录认证,鉴权授权
文章目录 前言一、shiro简介二、环境搭建2.1.数据库2.1.1user用户表2.1.2user_role用户角色关系表2.1.3role角色表2.1.4role_permission角色权限关系表2.1.5permission权限表 2.2导坐标2.3实体类2.3.1User2.3.2Role2.3.3Permission 2.4MVC三层2.4.1User2.4.1.1mapper层2.4.1.2s…...
Airbnb开源数据可视化工具Visx
一、什么是visx visx 是用于 React 的富有表现力的底层可视化组件集合,结合了 d3 的强大功能来生成可视化,以及 React 更新 DOM 的诸多优势。 在 Airbnb 内部,visx 的目标是统一整个公司的可视化堆栈,在此过程中,创建了 visx 项目,从而有效的将 D3 的强大功能与 React …...
VR仿真实训系统编辑平台赋予老师更多自由和灵活性
为了降低院校教师在VR虚拟现实方面应用的门槛,VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验,制作了VR动物课件编辑器,正在逐渐受到师生们的关注和应用。 简单来说,VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…...
父类对象转成子类对象
import org.springframework.beans.BeanUtils; import java.util.ArrayList; import java.util.List; public class Test {public static void main(String[] args) {List<B> bList new ArrayList<>();B b new B("a","这是a","b",…...
Spring Boot中如何使用Flyway进行数据库迁移
在本文中,我们将了解如何使用 Flyway 来管理 Spring Boot 应用程序中的 SQL 数据库架构。 在本文中,我们将了解如何使用 Flyway 来管理Spring Boot应用程序中的SQL 数据库架构。 Flyway是一个数据库迁移工具,它提供迁移历史和回滚的功能&…...
web在线编辑器(vue版)
目录 前言一、monaco-editor1、源码2、体积优化 二、ace-editor?1、源码2、体积优化 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多…...
【论文阅读】 Model Sparsity Can Simplify Machine Unlearning
Model Sparsity Can Simplify Machine Unlearning 背景主要内容Contribution Ⅰ:对Machine Unlearning的一个全面的理解Contribution Ⅱ:说明model sparsity对Machine Unlearning的好处Pruning方法的选择sparse-aware的unlearning framework Experiments…...
Spring Clould 部署 - Docker
视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) 初识Docker-什么是Docker(P42,P43) 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&…...
linux--链表动态创建
头插法: 核心代码: s->next head->next; head->next s; 尾插法 核心代码: tail head; s->next NULL; tail->next s; tail s; 当用头插法依次插入值分别为1,2,3,4,5的结点后, 单链表顺序为: he…...
iBooker 布客技术评论 20230818
一、程序员自检手册 为了避免焦虑,你首先需要做的就是梳理你的业务: (1)你所在的行业是轻资产还是重资产? 重资产就是人绕着机器转,创业需要买一大堆设备。如果是重资产,赶紧换一个。 &…...
CK-A60180、CK-B1542、CK-L3095单向离合器
CK-A1542、CK-A1747、CK-A2052、CK-A2652、CK-A3072、CK-A3580、CK-A4090、CK-A45100、CK-A450110、CK-A60130、CK-A65140、CK-A70150、CK-A75160、CK-A80170、CK-A1250、CK-A1855、CK-A2060、CK-A2563、CK-A2563T、CK-A2870、CK-A3080T、CK-A3585、CK-A35100、CK-A35140、CK-A…...
单因素多变量方差分析
多变量方差分析:是对多个独立变量是否受单个或多个因素影响而进行的方差分析。它不仅能够分析多个因素对观测变量的独立影响,更能够分析多个因素的交互作用能否对观测变量产生影响。本章以单因素多变量分析为例,即一个分组变量和多个欲分析的…...
Python Web:Django、Flask和FastAPI框架对比
原文:百度安全验证 Django、Flask和FastAPI是Python Web框架中的三个主要代表。这些框架都有着各自的优点和缺点,适合不同类型和规模的应用程序。 1. Django: Django是一个全功能的Web框架,它提供了很多内置的应用程序和工具&am…...
【CI/CD】Rancher K8s
Rancher & K8s Rancher 和 K8s 的关系是什么?K8s 全称为 Kubernetes,它是一个开源的,用于管理云平台中多个主机上的容器化的应用。而 Rancher 是一个完全开源的企业级多集群 Kubernetes 管理平台,实现了 Kubernetes 集群在混合…...
nodejs 之 express 实现下载网络图片并上传到七牛云对象存储oss空间
为方便阅读,本文将所有逻辑放在一个函数里,可根据自己的情况拆分。 安装依赖 在项目根目录下运行以下命令安装依赖 npm install express qiniu axios业务逻辑 在项目根目录下创建一个名为 app.js 的文件,并添加以下内容 const express re…...
综合能源系统(7)——综合能源综合评估技术
综合能源系统关键技术与典型案例 何泽家,李德智主编 综合能源系统是多种能源系统非线性耦合的、多时间与空间尺度耦合的“源-网-荷一储”一体化系统,通过能源耦合、多能互补,能够实现能源的高效利用,并提高新能源的利用水平。对…...
【JS 线性代数算法之向量与矩阵】
线性代数算法 一、向量的加减乘除1. 向量加法2. 向量减法3. 向量数乘4. 向量点积5. 向量叉积 二、矩阵的加减乘除1. 矩阵加法2. 矩阵减法3. 矩阵数乘4. 矩阵乘法 常用数学库 线性代数是数学的一个分支,用于研究线性方程组及其解的性质、向量空间及其变换的性质等。在…...
配置 yum/dnf 置您的系统以使用默认存储库
题目 给系统配置默认存储库,要求如下: YUM 的 两 个 存 储 库 的 地 址 分 别 是 : ftp://host.domain8.rhce.cc/dvd/BaseOS ftp://host.domain8.rhce.cc/dvd/AppStream vim /etc/yum.repos.d/redhat.repo [base] namebase baseurlftp:/…...
Docker容器与虚拟化技术:Docker资源控制、数据管理
目录 一、理论 1.资源控制 2.Docker数据管理 二、实验 1.Docker资源控制 2.Docker数据管理 三、问题 1.docker容器故障导致大量日志集满,造成磁盘空间满 2、当日志占满之后如何处理 四、总结 一、理论 1.资源控制 (1) CPU 资源控制 cgroups࿰…...
python生成器有几种写法,python生成器函数例子
大家好,小编来为大家解答以下问题,python生成器有几种写法,python生成器函数例子,今天让我们一起来看看吧! 本文部分参考:Python迭代器,生成器–精华中的精华 https://www.cnblogs.com/deeper/p…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...



