5、springboot3 vue3开发平台-后端- satoken 整合

news2025/1/17 22:02:31

文章目录

  • 1. 为什么使用sa-token
  • 2. 依赖导入jichu
    • 2.1 基础依赖引入
    • 2.2 redis整合
    • 2.3 redis 配置, 使redis能支持中文存储
  • 3. 配置
  • 4. 配置使用
    • 4.1 权限加载接口实现, 登录实现
    • 4.2 配置全局过滤器
    • 4.3 登录异常处理
  • 5. 登录测试
  • 6. 用户session的获取

1. 为什么使用sa-token

使用简单, 自由度相对较高, 不想费工夫配shior, 不想费脑子学SpringSecurity
官网: https://sa-token.cc/doc.html#/up/integ-redis

2. 依赖导入jichu

2.1 基础依赖引入

父模块引入
在这里插入图片描述
support模块引入依赖:

 		<dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-spring-boot3-starter</artifactId>
        </dependency>

若不需要整合redis的话引入上述依赖即可, 用户信息会存到框架内置CurrentHashMap中

2.2 redis整合

windows redis 安装参考: https://blog.csdn.net/qq_51355375/article/details/140726275

  • 为什么使用redis , 官网解释很好,抄过来:
Sa-Token 默认将数据保存在内存中,此模式读写速度最快,且避免了序列化与反序列化带来的性能消耗,
但是此模式也有一些缺点,比如:
	1、重启后数据会丢失。
	2、无法在分布式环境中共享数据。
为此,Sa-Token 提供了扩展接口,你可以轻松将会话数据存储在一些专业的缓存中间件上(比如 Redis), 做到重启数据不丢失,而且保证分布式环境下多节点的会话一致性。
sa提供两种方式整合,官网也说的很清楚, 这里使用第二种

在这里插入图片描述

注: 集成 Redis 后,框架自动保存数据。集成 Redis 只需要引入对应的 pom依赖 即可,框架所有上层 API 保持不变, 所以集成redis 影响的只是框架内部数据存储,对于使用框架来说用不用redis完全相同。

在父工程引入依赖版本控制:
在这里插入图片描述
support模块引入依赖:

		 <dependency>
            <groupId>cn.dev33</groupId>
            <artifactId>sa-token-redis-jackson</artifactId>
        </dependency>

记得引入连接池:

<!-- 提供Redis连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2.3 redis 配置, 使redis能支持中文存储

package com.ylp.support.config.reidis;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置类,更换默认序列化器
 *
 */
@Configuration
public class RedisConfig {
    /**
     * 创建 RedisTemplate Bean,用于操作 Redis 数据库。
     *
     * @param connectionFactory Redis 连接工厂
     * @return RedisTemplate 实例
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置 RedisTemplate 的连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);
        // 创建 StringRedisSerializer 实例,用于序列化和反序列化 Redis 的键和哈希键
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 创建 GenericJackson2JsonRedisSerializer 实例,用于序列化和反序列化 Redis 的值和哈希值
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置默认序列化器为 StringRedisSerializer
        redisTemplate.setDefaultSerializer(stringRedisSerializer);
        // 设置 RedisTemplate 的键序列化器为 StringRedisSerializer
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // 设置 RedisTemplate 的哈希键序列化器为 StringRedisSerializer
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // 设置 RedisTemplate 的值序列化器为 GenericJackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        // 设置 RedisTemplate 的哈希值序列化器为 GenericJackson2JsonRedisSerializer
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        return redisTemplate;
    }
}

3. 配置

spring:
  # 数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource    # 使用druid连接池
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/admin_system?serverTimezone=UTC
    username: root
    password: root
    
  # redis配置 
  redis:
    # Redis数据库索引(默认为0
    database: 1
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    # password: 
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0  
          
sa-token:
  # token 名称(同时也是 cookie 名称)
  token-name: satoken
  # token 有效期(单位:秒) 默认30天,-1 代表永久有效
  timeout: 2592000
  # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
  active-timeout: -1
  # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
  is-concurrent: true
  # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
  is-share: true
  # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
  token-style: uuid
  # 是否输出操作日志
  is-log: true
  is-read-cookie: false      # 是否从Cookie中读取Token,设置为false
  is-read-header: true         # 是否从Header中读取Token,设置为true

4. 配置使用

关于sa配置和使用相关的类文件按项目结构存放, 这里放在sys模块auth包下,包含sa配置使用,登录鉴权信息等
在这里插入图片描述

4.1 权限加载接口实现, 登录实现

获取当前账号权限码集合, 需要实现StpInterface接口, 缓存当前用户权限角色信息

package com.ylp.sys.auth.config;

import cn.dev33.satoken.stp.StpInterface;
import com.ylp.sys.auth.service.impl.AuthServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 自定义权限加载接口实现类
 */
@Component    // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
    private AuthServiceImpl authService;

    @Autowired
    public void setAuthService(AuthServiceImpl authService) {
        this.authService = authService;
    }

    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限
        List<String> list = authService.getPermissionListByUserId((Long) loginId);
        return list;
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色
        List<String> list = authService.getRoleLabelListByUserId((Long) loginId);
        return list;
    }
}

在AuthServiceImpl 中编写根据用户id 加载 权限和角色列表的函数, 账号密码检验成功调用 StpUtil.login(user.getId()) 登录, 并获取生成的token信息返回给前端。

package com.ylp.sys.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ylp.common.constants.HttpStatus;
import com.ylp.common.exception.ServiceException;
import com.ylp.common.response.Result;
import com.ylp.common.utils.StringUtils;
import com.ylp.sys.auth.config.cache.CurrentHashMapManager;
import com.ylp.sys.auth.entity.LoginDto;
import com.ylp.sys.auth.entity.LoginVo;
import com.ylp.sys.auth.entity.UserInfo;
import com.ylp.sys.auth.service.AuthService;
import com.ylp.sys.auth.utils.BCryptUtils;
import com.ylp.sys.auth.utils.ValidataCodeUtil;
import com.ylp.sys.domain.entity.*;
import com.ylp.sys.mapper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class AuthServiceImpl implements AuthService {
    private static final Logger log = LoggerFactory.getLogger(AuthServiceImpl.class);
    private final SysUserMapper userMapper;
    private final CurrentHashMapManager currentHashMapManager;

    public AuthServiceImpl(SysUserMapper sysUserMapper, CurrentHashMapManager currentHashMapManager) {
        this.userMapper = sysUserMapper;
        this.currentHashMapManager = currentHashMapManager;
    }

    @Override
    public LoginVo login(LoginDto loginDto) {
        SysUser user = getUserByName(loginDto.getUsername());
        if (ObjectUtil.isNull(user)) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED,"认证失败!");
        }
        if (!BCryptUtils.checkpw(loginDto.getPassword(), user.getPassword())) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED,"密码错误!");
        }
        LoginVo loginVo = null;
        try {
            loginVo= getLoginVo(user);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(HttpStatus.UNAUTHORIZED, "用户相关信息错误");
        }
        // 登录,获取tokenInfo , 存储用户session
        StpUtil.login(user.getId());    // 登录
        // 设置用户信息
        StpUtil.getSession().set("userInfo", new UserInfo(user.getId(), user.getUsername()));
        // 获取token
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        loginVo.setSaTokenInfo(tokenInfo);
        return loginVo;
    }

    /**
     * 根据账号查用户
     * @param userName
     * @return
     */
    public SysUser getUserByName(String userName) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotEmpty(userName), SysUser::getUsername, userName);
        wrapper.eq(SysUser::getStatus, 0);
        return userMapper.selectOne(wrapper);
    }

    private LoginVo getLoginVo(SysUser user) {
        LoginVo loginVo = new LoginVo();
        loginVo.setUserInfo(user);
        // 查询角色标识
        List<String> roleLabelList = getRoleLabelListByUserId(user.getId());
        loginVo.setRoleLabelList(roleLabelList);
        // 权限列表
        List<String> permissionList = getPermissionListByUserId(user.getId());
        loginVo.setPermissionList(permissionList);
        return loginVo;
    }

    /**
     * 根据用户id查询权限列表
     * @param userId
     * @return
     */
    public List<String> getPermissionListByUserId(Long userId) {
        List<String> perms = new ArrayList<>();
        try {
            perms = userMapper.selectPermsByUserId(userId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return perms;
    }


    /**
     * 根据用户id查角色标识列表
     * @param userId
     * @return
     */
    public List<String> getRoleLabelListByUserId(Long userId) {
        List<String> roleLabelList = new ArrayList<>();
        try {
           roleLabelList = userMapper.selectRoleLabelByUserId(userId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return roleLabelList;
    }
}

usermapper接口:

public interface SysUserMapper extends BaseMapper<SysUser> {
    /**
     * 根据用户id获取权限咧列表
     * @param id
     * @return
     */
    @Select("SELECT m.perms FROM sys_user u JOIN sys_user_role ur ON u.id = ur.user_id " +
            "JOIN sys_role_menu rm ON ur.role_id = rm.role_id " +
            "JOIN sys_menu m ON rm.menu_id = m.id where u.id = #{id};")
    List<String> selectPermsByUserId(@Param("id")Long id);

    @Select("SELECT r.role_label from sys_user_role ur JOIN sys_role r ON ur.role_id = r.id WHERE ur.user_id = #{id}")
    List<String> selectRoleLabelByUserId(@Param("id") Long id);

}

AuthController 调用接口:

package com.ylp.sys.auth.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.ylp.common.response.Result;
import com.ylp.sys.auth.entity.LoginDto;
import com.ylp.sys.auth.entity.LoginVo;
import com.ylp.sys.auth.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;

@Tag(name = "登录认证接口")
@RestController
@RequestMapping("/auth")
public class AuthController {
    private final AuthService authService;

    public AuthController(AuthService authService) {
        this.authService = authService;
    }

    @PostMapping("/login")
    @Operation(summary = "用户登录")
    public Result<LoginVo> login(@RequestBody LoginDto loginDto) {
        LoginVo loginVo = authService.login(loginDto);
        return Result.success("登录成功", loginVo);
    }
    
    @Operation(summary = "退出登录")
    @GetMapping("loginOut")
    public Result<String> loginOut() {
        StpUtil.logout();
        return Result.success("退出登录", null);
    }
}

4.2 配置全局过滤器

package com.ylp.sys.auth.config;

import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 配置放行资源
        // 无需拦截的接口集合
        List<String> ignorePath = new ArrayList<>();
        // knife4j(swagger)
        ignorePath.add("/swagger-resources/**");
        ignorePath.add("/swagger-ui.html");
        ignorePath.add("/doc.html");
        ignorePath.add("/v3/**");
        ignorePath.add("/webjars/**");
        ignorePath.add("/static/**");
        ignorePath.add("/templates/**");
        ignorePath.add("/error");
        // 登录页面
        ignorePath.add("/auth/login");
       

        // 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
        registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
                .addPathPatterns("/**")
                .excludePathPatterns(ignorePath);
    }
}

4.3 登录异常处理

在全局异常中拦截sa登录异常,返回给前端信息

package com.ylp.support.config.execption;

import cn.dev33.satoken.exception.NotLoginException;
import com.ylp.common.exception.ServiceException;
import com.ylp.common.response.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


/**
 * 全局异常处理
 */

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public Result handleServiceException(ServiceException e)
    {
        log.error(e.getMessage(), e);
        return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
    }

    /**
     * 服务异常
     */
    @ExceptionHandler(Exception.class)
    public Result handlerException(Exception e) {
        log.warn(e.getMessage());
        return Result.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "服务异常");
    }


    /**
     * 登录异常
     * @param notLoginException
     * @return
     */
    @ExceptionHandler(NotLoginException.class)
    public ResponseEntity<Result> handleNotLoginException(NotLoginException notLoginException) {
        Result result = Result.error("请登录");
        // 构建响应体
        return new ResponseEntity<>(result, HttpStatus.UNAUTHORIZED);
    }
}

5. 登录测试

使用knife4j 测试
在这里插入图片描述
登录成功,查看redis内容已缓存用户登录信息和session,token信息:
在这里插入图片描述

6. 用户session的获取

	@Operation(summary = "查询当前用户信息")
    @GetMapping("/currentUser")
    public Result<SysUser> currenUser() {
        UserInfo userLoginInfo = (UserInfo) StpUtil.getSession().get("userInfo");
        Long id = userLoginInfo.getId();
        return userService.getUserById(id);
    }

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

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

相关文章

MySQL索引与存储引擎、事物

数据库索引 是一个排序的列表&#xff0c;存储着索引值和这个值所对应的物理地址 无须对整个表进行扫描&#xff0c;通过物理地址就可以找到所需数据 是表中一列或者若干列值排序的方法 需要额外的磁盘空间 类型 普通索引 最基本的索引类型&#xff0c;没有唯一性之类的限制 创…

图不连通怎么办?

目录 1.问题 2.连通的相关概念 3.解决方案 C语言示例实现&#xff1a; 1.问题 无论是图的深度还是广度遍历都是从图的某一条边往下走&#xff0c;那么被孤立的结点怎么被遍历到呢&#xff1f; 2.连通的相关概念 连通&#xff1a;如果从V到W存在一条&#xff08;无向&#…

3D魔方游戏制作lua迷你世界

--3D魔方 --星空露珠工作室 --核心脚本来自负负 --1:xy 2:yx 3:xz 4:zx 5:yz 6:zy --4000,0-3 3995-0,3 local trn{ {{5,2},{3,1},{1,2},{1,3},{4,0},{2,2}}, {{3,0},{5,3},{1,3},{1,2},{2,3},{4,1}}, {{4,2},{2,1},{1,1},{1,0},{3,3},{5,1}}, {{2,0},{4,3},{1,0},{1,1},{5,0},…

Web3.js 4.x版本事件监听详解:从HTTP到WebSocket的迁移

项目场景 在一个使用以太坊区块链技术的项目中&#xff0c;需要监听智能合约的事件&#xff0c;以便在事件触发时能够及时响应。项目中使用了web3.js库的4.x版本&#xff0c;节点使用Geth启动&#xff0c;并通过HTTP与节点进行通信。 问题描述 合约DataStorage.sol文件已经定…

华为项目管理工具集

华为项目管理10大模板是一套被广泛认可和使用的项目管理工具集&#xff0c;它包含了在项目管理过程中常用的各种表格和文档模板。这些模板旨在帮助项目经理更有效地规划、执行和监控项目&#xff0c;确保项目的成功交付。 虽然具体的模板内容可能会有所不同&#xff0c;但根据…

51 单片机的Keil5软件

1. KEIL C51 软件获取 博主网盘下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1YBfrRh2L7SIehS5xLQkAow?pwd4211 提取码&#xff1a;4211 也可以在 KEIL 的官网上下载&#xff1a;http:// https://www.keil.com/download/product/ 打开界面如下图所示&#xff1…

机器学习(二十三):决策树和决策树学习过程

一、决策树 下面是数据集&#xff0c;输入特征是耳朵形状、脸形状、是否有胡子&#xff0c;输出结果是是否为猫 下图是决策树&#xff0c;根据耳朵形状、脸形状、是否有胡子这几个特征&#xff0c;建立决策树&#xff0c;从根节点一步步预测结果。 上图中&#xff0c;每一个椭…

[硬件]—电感传感器

电感传感器 1.概述 工作基础&#xff1a;电磁感应&#xff0c;即利用线圈自感或互感的改变来实现非电量测量。工作原理&#xff1a; 被测物理量&#xff08;非电量&#xff1a;位移、振动、流量&#xff09;&#xff1b;线圈自感系数L/互感系数M&#xff1b;电压或电流&#…

QT常用的控件(二)

QT的常用控件 一.按钮类控件1.1 Push Button代码示例: 带有图标的按钮代码示例: 带有快捷键的按钮代码示例: 按钮的重复触发 1.2 Radio Button代码示例: 选择性别代码示例: click, press, release, toggled 的区别代码示例: 单选框分组 1.3 Check Box代码示例: 获取复选按钮的取…

邮件攻击案例系列四:某金融企业遭遇撒网式钓鱼邮件攻击

案例描述 2023 年 3 月末&#xff0c;某知名投资公司业务经理李先生先后收到两封看似是来自邮件服务商和公司网络安全部门发出的邮件&#xff0c;标题是“紧急&#xff1a;邮箱安全备案更新通知”。邮件内容称&#xff0c;由于最近公司内部系统升级&#xff0c;所有员工必须重…

【微信小程序实战教程】之微信小程序的配置文件详解

小程序的配置文件 对于有过服务端开发的程序员来说&#xff0c;肯定对“约定优于配置”并不陌生&#xff0c;这是一种按约定编程的软件设计范式&#xff0c;目的在于减少软件开发者做决定的数量。而微信小程序正好与这种软件设计范式的理念相反&#xff0c;小程序是一种“配置…

java将map转json字符串或者再将json字符串转回map,java将对象转json字符串或者互想转换,对象集合和json字符串互转

1.导入hutool工具依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency>2.直接复制一下代码运行 import cn.hutool.json.JSONUtil;import java.util.Ar…

【C语言报错已解决】Format String Vulnerability

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言 在日常开发中&#xff0c;我们经常会遇到各种各样的bug&#xff0c;其中格式化字符串漏洞报错可能是最让人头疼的一种。这…

谷歌(google)又出新功能了,快来学

谷歌开发客户这个算是每个外贸业务必备的几个基础技能之一了&#xff0c;大家对谷歌这块的用法多少都有些了解。最近谷歌更新了一个功能&#xff0c;类似于商机推荐的功能&#xff0c;我带大家来了解一下。 我们搜索公司之后&#xff0c;他会展示其他用户搜索过的一些信息。大家…

高职院校大数据人才培养成果导向系统构建、实施要点与评量方法

一、引言 在当今信息化快速发展的背景下&#xff0c;大数据已成为推动社会进步和产业升级的重要力量。为满足社会对大数据人才的需求&#xff0c;高职院校纷纷开设大数据相关专业&#xff0c;并致力于探索科学有效的人才培养模式。本文立足于我国信息化与智能化发展趋势&#…

彻底解决WPS右键没有新建文件的问题

1、综合解决 这个教程能解决大部分的问题彻底解决WPS右键没有新建文件的问题 2、作者补充 作者的教程没有那么麻烦

【java】 力扣 最后一个单词的长度

目录 题目链接题目描述代码 题目链接 58.最后一个单词的长度 题目描述 代码 public int lengthOfLastWord(String s) {int n s.length();int count 0;if(s null ||s.length() 0){return 0;}for(int i n-1;i>0;i--){if(s.charAt(i) ){if(count 0) continue;break;}count…

在pytroch中使用CIFAR10完成完整的模型训练套路

模型训练套路&#xff1a; 1.准备数据集2.加载数据集3.搭建神经网络4创建损失函数5.优化器6.设置训练网络的一些参数7.添加tensorboard&#xff08;方便观察&#xff09;8.开始训练.测试9.保存神经网络 准备数据 #准备数据集 dataset_traintorchvision.datasets.CIFAR10("…

外星人入侵_外星人

项目_外星人入侵_外星人 1创建第一个外星人1.1创建Alien类1.2创建Alien实例1.3让外星人出现在屏幕上 2创建一群外星人2.1确定一行可以容纳多少外星人2.2 创建多行外星人2.3创建外星人群2.4重构create_fleet()2.5添加行 3让外星人群移动3.1向右移动外星人3.2创建表示外星人移动方…

迷你世界魔方模型快速制作

做六个不一样颜色的顶部 --黄&#xff0c;绿&#xff0c;红&#xff0c;蓝&#xff0c;橙&#xff0c;白 --local ids{4000,3999, 3998,3997,3996,3995} 游戏脚本运行上一期文章 local x0,y0,z0-39,7,10--起点坐标 --框架、底面、侧面1-4、顶面 local id{682,671,681,680,66…