用户中心——比如:腾讯的QQ账号可以登录到很多应用当中 02
文章目录
前端登录注册
blankTarget 表示是一个用户点击时跳转时,是打开一个新的页面还是,在本地页面覆盖。
constants 公共常量——》整体修改,整体替换。@
在 js 表示 str 根目录
target=“_blank” 表示跳转,打开一个新页面
Shit+F6 整体重构
前端解决跨域问题
前端-正向代理
**正向代理:**替客户端向服务器发送请求
**反向代理:**替服务器接收请求。
怎么实现代理:
- Nginx 服务器
- Node.js 服务器
spring:
server:
servlet:
context-path: /api
测试,测试
shifrt + F6 重构
右键查看-本地历史版本之前的
用户注销
前端用户管理页面
前端用户的权限管理
app.tsx 项目全局入口文件,定义了整个项目中都能使用的公共数据。
access.ts 控制用户的访问权限
首次访问页面(刷新页面),进入 app.tsx,执行 getInitialState ,该方法就是返回值就是全局可用的状态。
ProComponents 高级表单
- 通过 clumns 定义表格有哪些列
- columns 属性
- dataIndex 对应后端返回数据对象(注意一定要和后端设置的字段保持一致(后端返回给前端的字段信息保持一致))
- title 表格列名
- copyable 是否允许复制
- ellipsis 是否允许缩略
- valueType 用于声明这一列的类型,为什么样的类型(date,select<可枚举的值>)
用户注销:
package com.rainbowsea.usercenter.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rainbowsea.usercenter.common.ErrorCode;
import com.rainbowsea.usercenter.exception.BusinessException;
import com.rainbowsea.usercenter.model.User;
import com.rainbowsea.usercenter.service.UserService;
import com.rainbowsea.usercenter.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import static com.rainbowsea.usercenter.contant.UserConstant.USER_LOGIN_STATE;
/**
* @author huo
* @description 针对表【user(用户)】的数据库操作Service实现
* @createDate 2025-04-14 16:03:21
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
/**
* 加盐 混淆,让密码更加没有规律,更加安全一些
*/
private static final String SALT = "rainbowsea";
@Resource
private UserMapper userMapper;
/**
* 用户注销
*
* @param request
* @return 返回 1 表示注销成功
*/
@Override
public int userLogout(HttpServletRequest request) {
// 移除用户存储在 session 会话当中的用户态
request.getSession().removeAttribute(USER_LOGIN_STATE);
return 1;
}
}
用户代码优化——用户校验
仅适用于用户可信的情况。
后端优化:
- 通用返回对象:(BaseResponse)
- 目的:给对象补充一些信息,告诉前端整个请求在业务层面上是成功还是失败。
- 自定义错误码
- 返回类正常和错误信息。
{
"name": "yupi"
}
↓
// 成功
{
"code": 0 // 业务状态码
"data": {
"name": "yupi"
},
"message": "ok"
}
// 错误
{
"code": 50001 // 业务状态码
"data": null
"message": "用户操作异常、xxx"
}
- 封装全局异常处理(ErrorCode 枚举错误)
- 定义业务异常类
- 相对于 Java 的异常类,支持更多字段
- 自定义构造函数,更灵活/快捷的设置字段
- 定义业务异常类
package com.rainbowsea.usercenter.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 通用返回类 ,返回给前端的报错,提示信息的类
*
* @param <T> data 返回的类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseResponse<T> implements Serializable {
private static final long serialVersionUID = 7190730665908854521L;
private int code;
private T data;
private String message;
private String description;
public BaseResponse(int code, T data, String description) {
this(code, data, "", description);
}
public BaseResponse(int code, T data) {
this(code, data, "", "");
}
public BaseResponse(ErrorCode errorCode) {
this(errorCode.getCode(), null, errorCode.getMessage(), errorCode.getDescription());
}
}
package com.rainbowsea.usercenter.common;
import lombok.Getter;
/**
* 通用错误码,枚举类
*
* @author Rainbowsea
*/
@Getter
public enum ErrorCode {
SUCCESS(0, "ok", ""),
PARAMS_ERROR(40000, "请求参数错误", ""),
NULL_ERROR(40001, "请求数据为空", ""),
NOT_ERROR(40100, "未登录", ""),
NO_AUTH(40101, "无权限", ""),
SYSTEM_ERROR(50000, "系统内部异常", "");
private final int code;
/**
* 状态码信息
*/
private final String message;
/**
* 具体的错误信息描述
*/
private final String description;
ErrorCode(int code, String message, String description) {
this.code = code;
this.message = message;
this.description = description;
}
// 枚举类,不可以提供设置: set 方法,但可以提供 get 方法,也需要提供 get 方法
}
package com.rainbowsea.usercenter.common;
/**
* 返回工具类
*/
public class ResultUtils {
/**
* 成功
*
* @param data
* @param <T>
* @return
*/
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(0, data, "ok");
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode) {
return new BaseResponse(errorCode);
}
/**
* 失败
*
* @param code
* @param message
* @param description
* @return
*/
public static BaseResponse error(int code, String message, String description) {
return new BaseResponse(code, null, message, description);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode, String message) {
return new BaseResponse(errorCode.getCode(), null, message);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode, String message, String description) {
return new BaseResponse(errorCode.getCode(), null, message, description);
}
}
package com.rainbowsea.usercenter.exception;
import com.rainbowsea.usercenter.common.ErrorCode;
import lombok.Getter;
/**
* 自定义异常类
*/
@Getter
public class BusinessException extends RuntimeException {
private int code;
private String message;
private String description;
public BusinessException(int code, String message, String description) {
super(message);
this.code = code;
this.message = message;
this.description = description;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
this.description = errorCode.getDescription();
}
public BusinessException(ErrorCode errorCode,String description) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
this.description = description;
}
}
2. 编写全局异常处理器
1. 捕获代码中所有的异常,内部消化,集中处理,让前端得到更详细的业务报错/信息。(不将 HTTP 500 这样的错误返回给前端,而是通过返回后端给出的特质信息。返回给前端,后端的报错信息给到了前端(特别人就知道我们的项目架构了))
2. 同时屏蔽掉项目的内部状态。
3. 集中记录日志,集中处理日志。
3. 这里利用:
1. Spring AOP : 在调用方法前后进行额外的处理。
- 全局请求日志和登录校验
package com.rainbowsea.usercenter.exception;
import com.rainbowsea.usercenter.common.BaseResponse;
import com.rainbowsea.usercenter.common.ErrorCode;
import com.rainbowsea.usercenter.common.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 当发生 BusinessException.class 这个方法处理
*
* @param e
* @return
*/
@ExceptionHandler(BusinessException.class)
public BaseResponse businessExceptionHandler(BusinessException e) {
log.error("BusinessException" + e.getMessage(), e);
return ResultUtils.error(e.getCode(), e.getMessage(), e.getDescription());
}
/**
* 当发生 RuntimeException.class 异常时,这个方法处理
*
* @param e
* @return
*/
@ExceptionHandler(RuntimeException.class)
public BaseResponse runtimeExceptionHandler(BusinessException e) {
log.error("RuntimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage(), "");
}
}
添加一个额外的 userRole ‘用户角色 0- 普通用户 1 - 管理员 2 - vip’
-- auto-generated definition
create table user
(
username varchar(256) null comment '用户昵称',
id bigint auto_increment comment 'id'
primary key,
userAccount varchar(256) null comment '账号',
avatarUrl varchar(1024) null comment '用户头像',
gender tinyint null comment '性别',
userPassword varchar(512) not null comment '密码',
phone varchar(128) null comment '电话',
email varchar(512) null comment '邮箱',
userStatus int default 0 not null comment '状态-0-正常',
createTime datetime default CURRENT_TIMESTAMP null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP null comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除 0 1(逻辑删除)',
userRole int default 0 not null comment '用户角色 0- 普通用户 1 - 管理员 2 - vip'
)
comment '用户';
注意这里:我们修改更新了数据表,所以我们后端对应上的 Bean 对象,以及相关 MyBatis 当中 SQL 语句的映射相关的 XML 文件也是需要对应更新上的。这里我们还是使用:IDEA 当中的一个 MyBatis-x 插件自动生成对应的相关的 SQL XML 的脚本信息
,同时还有一个脱敏处理的位置上,我们需要 set 设置一个 userRole 字段信息
- 优化将用户常量都封装到一个包当中,定义到为常量类,这里我们是一个接口,常量
package com.rainbowsea.usercenter.contant;
/**
* 用户常量
*/
public interface UserConstant {
/**
* 用户登录态键
*/
public static final String USER_LOGIN_STATE = "userLoginState";
// 接口当中默认就是 public static final
/**
* 默认权限
*/
int DEFAULT_ROLE = 0;
/**
* 管理员权限
*/
int ADMIN_ROLE = 1;
}
- 将重复出现多次的代码片段,提取处理作为一个方法,进行一个复用使用,提高其复用性
后端优化:将脱敏操作,提取处理,因为有很多地方需要用到这个脱敏的操作,这里我们将脱敏操作方法,定义到 serivce 接口当中,然后在实现该接口的方法。
对应前端返回的,这里我们的 search
查询接口,也是需要,将其脱敏的。不可以将,查询的信息,都返回给前端,需要脱敏(这里我们使用 Java Lambda 表达式进行一个过滤)
/**
* 根据用户的名字查询用户信息
*
* @param username 用户名
* @return 多个用户的 List 集合
*/
@GetMapping("/search")
public List<User> searchUsers(@RequestBody String username, HttpServletRequest request) {
// 仅仅管理员可以查询
if (!isAdmin(request)) {
return new ArrayList<>();
}
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (StringUtils.isNoneBlank(username)) {
queryWrapper.like("username", username);
}
// 这样返回的话,会将查询的数据表中所有信息都会返回给前端的,这里我们需要过滤一下
//return userService.list(queryWrapper);
List<User> userList = userService.list(queryWrapper);
return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
/* return userList.stream().map(user -> {
userService.getSafetyUser(user);
return user;
}).collect(Collectors.toList());*/
}
前端优化
- 对接后端的错误信息。
补充:中途遇到的错误异常
Spring Boot 启动失败:Failed to start bean ‘documentationPluginsBootstrapper’ 解决方案
友情链接:
Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPointerException
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
符号: 方法 setUserAccount(java.lang.String) 位置: 类型为com.rainbowsea.userc
原因是:lombok 版本问题对应不上。也有可能是你的 IDEA 没有安装上 lombok 插件
这里的解决方式是:修改 Spring Boot 的版本,让其依赖的 Lombok 对应上。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<!-- <version>2.2.6.RELEASE</version>-->
<relativePath/> <!-- lookup parent from repository -->
</parent>
todo 表示后续完成
to do 表示这里,后续优化,完善。 这样标记之后,我们可以通过搜索工具,后续搜索进行完善。
密码统一都是:12345678
最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”