[黑马头条]-登录实现思路

需求分析

在黑马头条项目中,登录有两种方式:一种是用户输入账号密码后登录,这种方式登陆后的权限很大,可以查看,也可以进行其他操作;另一种方式就是用户点击不登录,以游客的身份进入系统,但是权限不足,只可以查看帖子等信息,无法评论点准收藏等操作。

表结构分析

关于app端用户相关的内容较多,可以单独设置一个库leadnews_user:

登录需要用到的是ap_user表,表结构如下:

-- auto-generated definition
create table ap_user
(id                         int unsigned auto_increment comment '主键'primary key,salt                       varchar(32)      null comment '密码、通信等加密盐',name                       varchar(20)      null comment '用户名',password                   varchar(32)      null comment '密码,md5加密',phone                      varchar(11)      null comment '手机号',image                      varchar(255)     null comment '头像',sex                        tinyint unsigned null comment '0 男1 女2 未知',is_certification           tinyint unsigned null comment '0 未1 是',is_identity_authentication tinyint(1)       null comment '是否身份认证',status                     tinyint unsigned null comment '0正常1锁定',flag                       tinyint unsigned null comment '0 普通用户1 自媒体人2 大V',created_time               datetime         null comment '注册时间'
)comment 'APP用户信息表';

 表结构如下图所示:

tinyint类型:占1个字节,不指定unsigned(非负数),值范围(-128,127),指定了unsigned,值范围(0,255)
tinyint通常表示小范围的数值,或者表示true或false,通常值为0表示false,值为1表示true

项目中的持久层使用的mybatis-plus,一般都使用mybais-plus逆向生成对应的实体类

app_user表对应的实体类如下:

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** <p>* APP用户信息表* </p>** @author itheima*/
@Data
@TableName("ap_user")
public class ApUser implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 密码、通信等加密盐*/@TableField("salt")private String salt;/*** 用户名*/@TableField("name")private String name;/*** 密码,md5加密*/@TableField("password")private String password;/*** 手机号*/@TableField("phone")private String phone;/*** 头像*/@TableField("image")private String image;/*** 0 男1 女2 未知*/@TableField("sex")private Boolean sex;/*** 0 未1 是*/@TableField("is_certification")private Boolean certification;/*** 是否身份认证*/@TableField("is_identity_authentication")private Boolean identityAuthentication;/*** 0正常1锁定*/@TableField("status")private Boolean status;/*** 0 普通用户1 自媒体人2 大V*/@TableField("flag")private Short flag;/*** 注册时间*/@TableField("created_time")private Date createdTime;}

这个实体类名为 ApUser ,是一个Java类,用于映射数据库中的 ap_user 表,它包含了多个字段,如主键 id 、加密盐 salt 、用户名 name 、加密后的密码 password 、手机号 phone 、用户头像 image 、性别 sex 、认证状态 certification 、身份认证状态 identityAuthentication 、账户状态 status 、用户类型 flag 以及注册时间 createdTime ,其中 id 字段使用了MyBatis-Plus的 @TableId 注解来指定其为主键,并且设置为主键生成策略为自动增长,其他字段则使用 @TableField 注解来指定对应的数据库列名,类中还使用了lombok的 @Data 注解来自动生成getter和setter方法,以及 serialVersionUID 来支持序列化,整个类实现了 Serializable 接口,以支持对象的序列化操作。

思路分析

Md5相同的密码每次加密都一样,不太安全,所以在项目开发中我们一般会使用加盐来加强密码,思路如下:首先,前端根据用户名+密码来注册:

第一步:我们根据用户生成salt,这是一个随机的字符串用表中的salt来保存;

第二步:我们通过密码+salt来组成新的密码,经过md5加密后的密码保存在表password字段中;

第三步:当前端发来请求,我们会先根据用户名来数据库中查询用户;如果用户不为空,则取出salt的值和前端传递过来的密码拼接成新的密码;然后再跟数据库中的密码比对,如果二者密码一致,则用户验证成功;相反,如果比对不一致,则表示密码有误。

第四步:如果用户是首次登录的话,也就是是注册的话,后端是需要生成一个jwt令牌给前端的,前端将这个令牌保存好;之后前端的每一次请求,都需要携带这个令牌;在后端根据令牌是否存在并且令牌是否有效来判断请求是否可以正常执行。

用户游客登录,生成jwt返回(基于默认值0生成),当然这种方式登录 的权限没有账号密码登录的权限大,游客登录一般只有查看操作。

运营端微服务搭建

首先需要创建工程,在heima-leadnews-service下创建工程heima-leadnews-user,创建好之后需要建立对应的包,如图所示:

引导类

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.user.mapper")
public class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class,args);}
}

 这个引导类名为 UserApplication ,它是一个Spring Boot应用的启动类,使用了 @SpringBootApplication 注解来标识这是一个Spring Boot应用,并且包含了 @EnableAutoConfiguration 、 @ComponentScan 和 @Configuration 等注解的功能,用于自动配置Spring应用上下文和组件扫描。此外,它还使用了 @EnableDiscoveryClient 注解来启用Spring Cloud的客户端发现功能,这使得应用能够注册到服务发现中心,从而实现微服务架构中的服务发现。 @MapperScan 注解用于指定MyBatis的Mapper接口所在的包路径,以便Spring Boot自动扫描并注册这些Mapper接口。在 main 方法中,通过调用 SpringApplication.run 方法并传入当前类和命令行参数,来启动Spring Boot应用。这个类是整个应用的入口点,通过运行这个类,就可以启动整个Spring Boot应用并使其参与到微服务架构中。

bootstrap.yml

server:port: 51801
spring:application:name: leadnews-usercloud:nacos:discovery:server-addr: 192.168.200.130:8848config:server-addr: 192.168.200.130:8848file-extension: yml

在nacos中创建配置文件

代码如下:

spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: root
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径,通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.user.pojos

logback.xml

<?xml version="1.0" encoding="UTF-8"?><configuration><!--定义日志文件的存储地址,使用绝对路径--><property name="LOG_HOME" value="e:/logs"/><!-- Console 输出设置 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><charset>utf8</charset></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 异步输出 --><appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>512</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="FILE"/></appender><logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false"><appender-ref ref="CONSOLE"/></logger><logger name="org.springframework.boot" level="debug"/><root level="info"><!--<appender-ref ref="ASYNC"/>--><appender-ref ref="FILE"/><appender-ref ref="CONSOLE"/></root>
</configuration>

登录功能实现

①:接口定义

@RestController
@RequestMapping("/api/v1/login")
public class ApUserLoginController {@PostMapping("/login_auth")public ResponseResult login(@RequestBody LoginDto dto) {return null;}
}

 ②:持久层mapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.user.pojos.ApUser;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {
}

③:业务层service

import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;public interface ApUserService extends IService<ApUser>{/*** app端登录* @param dto* @return*/public ResponseResult login(LoginDto dto);}

实现类:

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;
import com.heima.user.mapper.ApUserMapper;
import com.heima.user.service.ApUserService;
import com.heima.utils.common.AppJwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;import java.util.HashMap;
import java.util.Map;@Service//标记为服务类
@Transactional//保证了事务的原子性
@Slf4j//方便了记录日志
//这段代码的主要功能是实现用户登录和游客登录的逻辑
//用户登录时,根据手机号查询用户,验证密码后返回用户的JWT令牌和用户信息
//游客登录时,返回id为0的游客JWT令牌
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {@Overridepublic ResponseResult login(LoginDto dto) {//1.正常登录(手机号+密码登录),如果前端传递的手机号和密码都不为空的话,执行以下操作if (!StringUtils.isBlank(dto.getPhone()) && !StringUtils.isBlank(dto.getPassword())) {//1.1查询用户,使用MyBatis Plus的getOne方法查询数据库中phone字段与dto.getPhone()相同的用户信息ApUser apUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));//先对从数据库中查询的对象进行判断,为空表示数据库没有登陆者信息,直接结束方法if (apUser == null) {//用统一封装结果集封装结果,返回提示给用户return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");}//1.2 比对密码//获取数据库用户的盐String salt = apUser.getSalt();//获取前端传递过来的代码String pswd = dto.getPassword();//将上述两个变量进行字符串拼接,然后再md5加密,赋值给pswdpswd = DigestUtils.md5DigestAsHex((pswd + salt).getBytes());//判断我们刚刚生成的密码和数据库中的密码是否一致if (!pswd.equals(apUser.getPassword())) {//不一致的话,通过统一封装结果集封装登录密码错误的信息会前端return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);}//密码比对成功,就需要给用户生成jet令牌//1.3 返回数据  jwtMap<String, Object> map = new HashMap<>();//用自定义的工具类AppJwtUtil给登录用户的id生成tokenmap.put("token", AppJwtUtil.getToken(apUser.getId().longValue()));//下面这两步是是为了保护用户隐私设置的,即将盐和密码置空,前端就查看不了密码//然后再将当前用户存入map集合中apUser.setSalt("");apUser.setPassword("");map.put("user", apUser);//最后,用统一封装结果集将map集合返回给前端return ResponseResult.okResult(map);} else {//2.游客登录,即没有账号密码  同样返回token  id = 0//我们约定如果是游客登录的话,直接使用0作为用户的id,生成tokenMap<String, Object> map = new HashMap<>();map.put("token", AppJwtUtil.getToken(0l));//直接将生成的token返回给前端展示return ResponseResult.okResult(map);}}
}

④:控制层controller

import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.dtos.LoginDto;
import com.heima.user.service.ApUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/v1/login")
public class ApUserLoginController {@Autowiredprivate ApUserService apUserService;@PostMapping("/login_auth")public ResponseResult login(@RequestBody LoginDto dto) {return apUserService.login(dto);}
}

思路分析:

这段代码实现了一个用户登录服务,其中包括正常用户登录和游客登录两种情况。对于正常用户登录,首先检查前端传递的手机号和密码是否都不为空,然后使用MyBatis Plus的查询方法根据手机号查询数据库中的用户信息,如果用户不存在则返回错误提示;如果用户存在,则进一步比对密码,通过将前端密码与数据库中的盐值拼接后进行MD5加密,与数据库中的密码进行比较,如果密码错误则返回错误提示;密码正确则生成JWT令牌,并将用户的敏感信息(盐和密码)置空后,连同令牌一起返回给前端。对于游客登录,直接生成一个id为0的JWT令牌并返回。整个过程中,使用了 @Transactional 注解来保证事务的原子性,使用 @Slf4j 注解来方便日志记录,同时通过自定义的工具类 AppJwtUtil 来生成JWT令牌,通过统一封装的结果集 ResponseResult 来返回操作结果。

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

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

相关文章

了解.NET Core状态管理:优化技巧与常见问题解决方案

前言 欢迎关注dotnet研习社&#xff0c;今天我们聊聊“ .NET Core 中的状态管理”。 在Web应用程序中&#xff0c;管理和维持状态是一个非常重要的主题&#xff0c;尤其是在无状态的环境中&#xff0c;如 HTTP 协议和 RESTful API。对于基于 .NET Core 构建的应用程序&#xff…

504网关超时可能是哪些原因导致?

在网络访问中&#xff0c;504 网关超时&#xff08;Gateway Timeout&#xff09;如同一个突然亮起的警示灯&#xff0c;打断用户的浏览或操作流程。这个 HTTP 状态码意味着服务器作为网关或代理时&#xff0c;未能在规定时间内收到上游服务器的响应。引发504错误的核心因素有哪…

ComfyUI 常见报错问题解决方案合集(持续更新ing)

前言&#xff1a; 本文汇总了 5 大高频问题 及其解决方案&#xff0c;涵盖&#xff1a; HuggingFace 认证修复&#xff08;Token 申请 手动下载指南&#xff09; ComfyUI 版本更新&#xff08;完整命令 依赖管理&#xff09; 自启动配置&#xff08;Conda 环境 权限修复&…

完美解决Linux服务器tomcat开机自启动问题

经过多次测试终于彻底解决tomcat开机自启动的问题了 PID3ps aux | grep /home/server/shichuan/ | grep java | awk {print $2} if [ -n "$PID3" ]; then 这个判断pid的方式还是可能出现启动失败的情况 # tail -n 1 /home/server/shichuan/logs/catalina.out |grep…

kotlin部分常用特性总结

<h3>Kotlin中类和对象初始化</h3><ul> <li>添加open关键字代表可以被继承</li> <li>Any 是所有类的父类,类似Object,包含 equals() hashCode() toString()方法</li> <li>constructor 关键字代表构造函数, constructor关键字可…

PHP 就业核心技能速查手册

# PHP 就业核心技能速查手册 > 高效聚焦市场所需&#xff0c;快速提升竞争力 --- ## 一、语法基础&#xff08;必会&#xff01;&#xff09; php // 1. 变量与数据类型 $price 19.99; // 浮点型 $isStock true; // 布尔型 // 2. 流程控制 foreach ($…

从混沌到秩序:数据科学的热力学第二定律破局——线性回归的熵减模型 × 最小二乘的能量最小化 × 梯度下降的负反馈控制系统,用物理定律重构智能算法的统一场论

目录 一、机器学习是什么&#xff1f; 1.1 什么是机器学习&#xff1f; 1.2 机器学习的三大类型 二、线性回归是什么&#xff1f; 2.1 通俗理解 2.2 数学表达 三、最小二乘法&#xff08;Least Squares Method&#xff09; 3.1 什么是损失函数&#xff1f; 3.2 什么是最小…

BI 数据可视化平台建设(3)—首页性能提升实践

作者&#xff1a; vivo 互联网大数据团队- Wang Lei 本文是vivo互联网大数据团队《BI 数据可视化平台建设》系列文章第3篇。 随着越来越多代码的堆积&#xff0c;平台的运行加载性能也在逐步下降&#xff0c;在不同程度上极大地影响了用户体验&#xff0c;从而导致用户流失。本…

基于Python的毕业设计选题管理系统设计与实现

基于Python的毕业设计选题管理系统设计与实现摘要本论文详细阐述了一个基于Python的毕业设计选题管理系统的设计与实现过程。该系统采用了Python的Tkinter库构建图形用户界面&#xff0c;使用SQLite数据库存储数据&#xff0c;实现了高校毕业设计选题过程中的教师出题、学生选题…

如何在HTML5页面中嵌入视频

在HTML5中嵌入视频主要使用<video>标签&#xff0c;这是一种简单且标准的方式。以下是详细步骤和示例&#xff1a; 基础实现 <!DOCTYPE html> <html> <head><title>视频嵌入示例</title> </head> <body><!-- 基础视频播放器…

java操作Excel两种方式EasyExcel 和POI

一、POI1.引入依赖<!-- 03 xls--> <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version> </dependency><!-- 07 xlsx --> <dependency><groupId>org.a…

Openlayers 面试题及答案180道(141-160)

《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。 前后端面试题-专栏总目录 文章目录 一、本文面试题目录 141. 如何在生产环境中优…

LangChain面试内容整理-知识点24:实战案例:智能助手 Agent 构建

本案例讲述如何用LangChain构建一个结合多个工具的智能助手 Agent。智能助手需要理解用户复杂请求,通过调用不同工具(如搜索、计算、查数据库等)执行多步推理,再给出答案。LangChain的Agent框架非常适合这种场景。 构建步骤: 确定需求和选择Agent类型:假设我们要一个能上…

【MATLAB例程】Taylor算法用于TOA(到达时间)的三维标签位置解算,可自适应基站数量。附下载链接

本文给出自适应锚点&#xff08;基站&#xff09;的Taylor算法解算TOA&#xff08;到达时间&#xff09;的MATLAB代码。参考论文&#xff1a;《基于Taylor-Chan算法的改进UWB室内三维定位方法》中的Taylor算法来解算TOA的复现程序&#xff08;MATLAB&#xff09;。 文章目录运行…

Eclipse代码折叠增强插件的安装与使用

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;Eclipse作为Java开发者的IDE&#xff0c;提供包括代码折叠在内的多种功能&#xff0c;便于管理与阅读代码。本文介绍的“com.cb.eclipse.folding_1.0.6.jar”插件能够进一步增强Eclipse的代码折叠能力。安装后&…

Python day18

浙大疏锦行 python day 18. 内容&#xff1a; 昨天学习了聚类算法的一些基本内容&#xff0c;今天继续学习相关知识分析簇的特征和相关含义&#xff08;使用可视化来进行分析&#xff0c;也可以使用ai&#xff09; 代码&#xff1a; shap.initjs() # 初始化 SHAP 解释器 ex…

WPS文档中心及文档中台远程命令执行漏洞

【严重】WPS文档中心及文档中台远程命令执行漏洞 漏洞描述 WPS文档中心是面向个人和企业的云端文档存储与管理平台&#xff0c;WPS文档中台是为企业提供的集成化文档协同与流程管理解决方案&#xff0c;强调API对接与业务系统整合。 在2024年5月之前通过docker私有化部署的版…

WPF 加载和显示 GIF 图片的完整指南

WPF 加载和显示 GIF 图片的完整指南 在 WPF 中加载和显示 GIF 图片需要一些特殊处理&#xff0c;因为 WPF 的 Image 控件默认不支持动画 GIF。 解决方案一&#xff1a;使用 WpfAnimatedGif 库&#xff08;推荐&#xff09; 这是最简单且功能最完整的方法。 实现步骤&#xff1a…

Node.js GET/POST请求详解

Node.js GET/POST请求详解 引言 Node.js作为一种基于Chrome V8引擎的JavaScript运行环境&#xff0c;以其高性能、非阻塞I/O模型和轻量级等特点&#xff0c;在服务器端开发中得到了广泛应用。本文将详细介绍Node.js中GET和POST请求的处理方法&#xff0c;帮助开发者更好地理解和…

C++string类(2)

3.string类对象的访问及遍历操作函数名称功能说明operator[] &#xff08;重 点&#xff09;返回pos位置的字符&#xff0c;const string类对象调用beginendbegin获取第一个字符的迭代器 end获取最后一个字符下一个位置的迭代器rbeginrendrbegin获取最后一个字符的迭代器 ren…