【SpringBoot开发】之商城项目案例(实现登陆版)

2023-12-31 09:50:15

?🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

? ? ? ? 在上一期的博客分享中我们初步的将项目搭建起来了,简单的实现了首页的数据数据库绑定查询显示,今天给大家带来的是登陆的功能实现,以及对其登陆功能的完善。

一、登陆功能实现代码编写

1. 导入数据回显的响应封装类

? ? ? ? 我们导入的是前面几期的博客中分享到的响应封装类,用户数据回显以及数据查询的状态。

?JsonResponseBody.java

package com.yx.yxshop.resp;

import lombok.Data;

@Data
public class JsonResponseBody<T> {

    private Integer code;
    private String msg;
    private T data;
    private Long total;

    private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {
        this.code = jsonResponseStatus.getCode();
        this.msg = jsonResponseStatus.getMsg();
        this.data = data;
    }

    private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {
        this.code = jsonResponseStatus.getCode();
        this.msg = jsonResponseStatus.getMsg();
        this.data = data;
        this.total = total;
    }

    public static <T> JsonResponseBody<T> success() {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, null);
    }

    public static <T> JsonResponseBody<T> success(T data) {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, data);
    }

    public static <T> JsonResponseBody<T> success(T data, Long total) {
        return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);
    }

    public static <T> JsonResponseBody<T> unknown() {
        return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);
    }

    public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {
        return new JsonResponseBody<T>(jsonResponseStatus, null);
    }

}

?JsonResponseStatus.java

package com.yx.yxshop.resp;

import lombok.Getter;

@Getter
public enum JsonResponseStatus {

    OK(200, "OK"),
    UN_KNOWN(500, "未知错误"),
    LOGIN_MOBILE_INFO(5001, "未携带手机号或手机号格式有误"),
    LOGIN_PASSWORD_INFO(5002, "未携带密码或不满足格式"),
    LOGIN_NO_EQUALS(5003, "登录信息不一致"),
    LOGIN_MOBILE_NOT_FOUND(5004, "登录手机号未找到"),
    ;

    private final Integer code;
    private final String msg;

    JsonResponseStatus(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getName(){
        return this.name();
    }

}

2. 控制层编写请求

? ? ? ? 在编写控制层的请求方法之前首先新建一个文件夹存放我们的vo视图类,vo实体类用于操作数据库的传参设置,避免改动对应表的实体对象类

UserVo.java
package com.yx.yxshop.vo;

import lombok.Data;

/**
 * com.yx.yxshop.vo
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/28
 * 确保实体类的正确性,用于请求操作
 */
@Data  //提供set/get方法
public class UserVo {

//    定义所需的属性
    private String phone;//手机号码

    private String password;//用户密码



}

? ? ? ? 借助我们的JsonResponseBody响应封装了进行编写登陆请求以及登陆的方法、接口以及接口实现类

?IUserService.java
package com.yx.yxshop.service;

import com.yx.yxshop.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.vo.UserVo;

/**
 * <p>
 * 用户信息表 服务类
 * </p>
 *
 * @author yangxin
 * @since 2023-12-27
 */
public interface IUserService extends IService<User> {

    JsonResponseBody<?> login(UserVo vo);

}
UserServiceImpl.java?
package com.yx.yxshop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.yxshop.pojo.User;
import com.yx.yxshop.mapper.UserMapper;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import com.yx.yxshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author yangxin
 * @since 2023-12-27
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {


    @Override
    public JsonResponseBody<?> login(UserVo vo) {


//        对其带来的参数对象进行判断
//        电话号码
        if (vo.getPhone()==null){
//                  返回没有手机号码的异常状态
        return JsonResponseBody.other(JsonResponseStatus.LOGIN_MOBILE_INFO);
        }
//        密码
        if (vo.getPassword()==null){
//            返回没有密码的异常状态
        return JsonResponseBody.other(JsonResponseStatus.LOGIN_PASSWORD_INFO);
        }
//     从数据库中查询用户信息
        User one = getOne(new QueryWrapper<User>()
                .lambda()                       //false表示有多条数据不报错
                .eq(User::getId, vo.getPhone()).eq(User::getPassword, vo.getPassword()), false);

        if (one==null){//为查询到指定数据
            return JsonResponseBody.other(JsonResponseStatus.LOGIN_NO_EQUALS);//返回登录信息不一致的
        }
//        登录成功   (查询到了指定的数据)
        return JsonResponseBody.success(one);
    }
}
?UserController.java
package com.yx.yxshop.controller;

import com.yx.yxshop.pojo.User;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.service.IUserService;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * 用户信息表 前端控制器
 * </p>
 *
 * @author yangxin
 * @since 2023-12-27
 */
@Controller
@RequestMapping("/user")
public class UserController {

// 引入用户接口类
    @Autowired
    private IUserService userService;

//    编写一个用户登陆的请求
    public JsonResponseBody<?> userLogin(UserVo vo){
        return userService.login(vo);
    }



}

? ? ? ? 代码编写到这里我们的登陆的基本功能已经基本的实现了,但是我们接下来会对其进行完善。

二、完善登陆之全局异常处理

? ? ? ? 我们直接对其抛出异常,但是我们的系统也会对其抛出异常。因此我们新建一个包,用于管理我们的全局异常。

?
BusinessException.java
package com.yx.yxshop.exp;

import com.yx.yxshop.resp.JsonResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * 错误异常处理类
 */
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BusinessException extends RuntimeException {

    private JsonResponseStatus jsonResponseStatus;

}

? ? ? ? 全局异常处理类,用于处理全局的异常捕捉。

?GlobalExceptionHandler.java
package com.yx.yxshop.exp;

import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice//增强类返回json数据
//@ControllerAdvice//增强类返回页面
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public JsonResponseBody<?> BusinessExceptionHandler(BusinessException e){
//        获取异常状态
        JsonResponseStatus status = e.getJsonResponseStatus();
//        返回json数据
        return JsonResponseBody.other(status);
    }

//    捕捉所有的异常
    @ExceptionHandler(Throwable.class)
    public JsonResponseBody<?> BusinessExceptionHandler(Throwable e){
//        返回json数据
        return JsonResponseBody.unknown();
    }
}

? ? ? ? 在接口实现类进行稍微的调整

? ? ? ? ?我们这里直接运行项目,然后直接访问登陆的请求

三、完善登陆之jsr303验证

? ? ? ? 集成jsr303验证首先要导入对应的pom文件依赖

<!--   导入jsr303依赖  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

?????????集成了之后我们在实体类进行对应的编写修改,在属性上打上注释进行标记

? ? ? ? ?在控制类进行标记使用

? ? ? ? ?在全局异常类中添加一个方法进行捕捉异常

? ? ? ? ?我们在网页上直接进行发送登陆请求

四、完善登陆之密码加密

1. 前端进行加密

? ? ? ? 首先在我们的登陆界面编写登陆的点击事件

<script>
<#--			设置登录的点击事件-->
			$("#login").click(()=>{
				// 获取到密码与手机号码
				let phone = $("#mobile").val();
				let password = $("#password").val();
			// 发送登陆的post请求
				$.post('${springMacroRequestContext.contextPath}/user/login',{
					// 传递参数
					phone,password
				},resq=>{//接受对象类型

				},"json")
			})


		</script>

? ? ? ? 接下来我们进行加密,首先我们先导入我们的js,选择的函数MD5加密

    <script src="http://www.gongjuji.net/Content/files/jquery.md5.js" type="text/javascript"></script>

? ? ? ? ?进行数据对其加密

?

? ? ? ? ?然后我们进入登陆,在控制天查看可以查看加密后的密码

? ? ? ? ?我们导入MD5的工具类,进行后台加密

?MD5Utils.java
package com.yx.yxshop.utils;

import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

@Component
public class MD5Utils {

    //加密盐,与前端一致
    private static final String salt = "f1g2h3j4";

    public static String md5(String src) {
        return DigestUtils.md5DigestAsHex(src.getBytes(StandardCharsets.UTF_8));
    }

    public static String createSalt() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 将前端的明文密码通过MD5加密方式加密成后端服务所需密码,混淆固定盐salt,安全性更可靠
     */
    public static String inputPassToFormPass(String inputPass) {
        String str = salt.charAt(1) + String.valueOf(salt.charAt(5)) + inputPass + salt.charAt(0) + salt.charAt(3);
        return md5(str);
    }

    /**
     * 将后端密文密码+随机salt生成数据库的密码,混淆固定盐salt,安全性更可靠
     */
    public static String formPassToDbPass(String formPass, String salt) {
        String str = salt.charAt(7) + String.valueOf(salt.charAt(9)) + formPass + salt.charAt(1) + salt.charAt(5);
        return md5(str);
    }

    public static void main(String[] args) {
        String formPass = inputPassToFormPass("123456");
        System.out.println("前端加密密码:" + formPass);
        String salt = createSalt();
        System.out.println("后端加密随机盐:" + salt);
        String dbPass = formPassToDbPass(formPass, salt);
        System.out.println("后端加密密码:" + dbPass);
    }

}

? ? ? ? 然后对其前台传来的密码进行二次加密,在service层进行加密

package com.yx.yxshop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.yxshop.exp.BusinessException;
import com.yx.yxshop.pojo.User;
import com.yx.yxshop.mapper.UserMapper;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import com.yx.yxshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;

import static com.yx.yxshop.resp.JsonResponseStatus.LOGIN_NO_EQUALS;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author yangxin
 * @since 2023-12-27
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {


    @Override
    public JsonResponseBody<?> login(UserVo vo) {

//     从数据库中查询用户信息
        User one = getOne(new QueryWrapper<User>()
                .lambda()                       //false表示有多条数据不报错
                .eq(User::getId, vo.getPhone()), false);//不带密码查询

        if (one==null){//为查询到指定数据
            throw new BusinessException(LOGIN_NO_EQUALS);//抛出登录信息不一致的
        }
//     加前端的密码+数据库的盐加密
        String str =vo.getPassword()+one.getSalt();
         str = DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));
//         判断
        if (!str.equals(one.getPassword())){//判断加密的和数据库对比
            throw new BusinessException(LOGIN_NO_EQUALS);//抛出异常
        }
//        登录成功   (查询到了指定的数据)
        return JsonResponseBody.success();
    }
}

? ? ? ? ?然后我们运行进行登陆测试

? ? ? ? 我们最后将登陆的请求方法完善在前端的JS中?

五、完善登陆之Redis集成使用

? ? ? ? 当我们登陆完成之后我们在后续的很多地方需要获取到用户信息进行操作,例如:添加购物车、下单、主页显示登陆用户,因此我们使用的是Redis来存储我们的登陆用户信息。

1. 导入Redis的pom依赖

<!--       导入Redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

? ? ? ? ?当然我们集成了redis之后还要将我们的Redis服务打开才能正常使用

2. 配置yml连接

? ? ? ? 在yml文件中进行我们redis相关的文件配置

    redis:
        host: 127.0.0.1
        port: 6379
        password: 123456
        database: 0

3. 集成使用?

? ? ? ? ?接下来就是对其集成Redis到我们的项目之中进行使用,在UserSeriviceImpl中进行使用

? ? ? ? ?考虑到后续存放我们的用于信息时我们的键重复时,因此我们借用使用雪花ID进行生成键。对其的导入其pom文件的依赖。

<dependency>
            <groupId>com.github.yitter</groupId>
            <artifactId>yitter-idgenerator</artifactId>
            <version>1.0.6</version>
        </dependency>

? ? ? ? ?接下来就是雪花ID与Redis的集成使用

? ? ? ? ?我们现在的项目并不是前后端分离的项目,所以前端要获取到用户的信息需要我们使用到Cookie存储,因此需要借助一些工具类

CookieUtils.java
package com.yx.yxshop.utils;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

@Slf4j
public class CookieUtils {

    /**
     * @Description: 得到Cookie的值, 不编码
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     * @Description: 得到Cookie的值
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * @Description: 得到Cookie的值
     */
    public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
     * @param request
     * @param response
     * @param cookieName
     * @param cookieValue
     * @param cookieMaxage
     * @Description: 设置Cookie的值 在指定时间内生效,但不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
    }

    /**
     * @Description: 设置Cookie的值 不设置生效时间,但编码
     * 在服务器被创建,返回给客户端,并且保存客户端
     * 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中
     * 如果没有设置,会默认把cookie保存在浏览器的内存中
     * 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
     * @Description: 设置Cookie的值 在指定时间内生效, 编码参数
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage, boolean isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }

    /**
     * @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                 String cookieValue, int cookieMaxage, String encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
     * @Description: 删除Cookie带cookie域名
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
                                    String cookieName) {
        doSetCookie(request, response, cookieName, null, -1, false);
    }


    /**
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                          String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
                String domainName = getDomainName(request);
                log.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description: 设置Cookie的值,并使其在指定时间内生效
     */
    private static void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                    String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0)
                cookie.setMaxAge(cookieMaxage);
            if (null != request) {// 设置域名的cookie
                String domainName = getDomainName(request);
                log.info("========== domainName: {} ==========", domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description: 得到cookie的域名
     */
    private static String getDomainName(HttpServletRequest request) {
        String domainName = null;

        String serverName = request.getRequestURL().toString();
        if (serverName == null || serverName.equals("")) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            if (serverName.indexOf(":") > 0) {
                String[] ary = serverName.split("\\:");
                serverName = ary[0];
            }

            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3 && !isIp(serverName)) {
                // www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }
        return domainName;
    }

    public static String trimSpaces(String IP) {//去掉IP字符串前后所有的空格
        while (IP.startsWith(" ")) {
            IP = IP.substring(1, IP.length()).trim();
        }
        while (IP.endsWith(" ")) {
            IP = IP.substring(0, IP.length() - 1).trim();
        }
        return IP;
    }

    public static boolean isIp(String IP) {//判断是否是一个IP
        boolean b = false;
        IP = trimSpaces(IP);
        if (IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {
            String s[] = IP.split("\\.");
            if (Integer.parseInt(s[0]) < 255)
                if (Integer.parseInt(s[1]) < 255)
                    if (Integer.parseInt(s[2]) < 255)
                        if (Integer.parseInt(s[3]) < 255)
                            b = true;
        }
        return b;
    }

}

? ? ? ? 导入完其帮助类时我们在UserServiceImpl进行运用使用,但是需要请求响应,在控制层进行带入。?

? ? ? ? 后面就是其运用了。?

? ? ? ? 在进行测试之前必须在实体类中要进行一个修改?

? ? ? ? ?导入一个我们Redis的配置类利于我们存储数据及数据查看

?RedisConfig.java
package com.yx.yxshop.config;

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;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

? ? ? ? 然后进行运行测试项目

?

? ? ? ? ?我们运行之后在网页进行访问页面,进行登陆的操作在网页的应用程序生成了一个Cookie和在Redis缓存中生成对应的用户信息。但是以后在完善代码的过程中要多次获取Cookie中的用户信息,我们把所涉及到的代码给他封装成一个类。

?IRedisService.java
package com.yx.yxshop.service;

import com.yx.yxshop.pojo.User;

/**
 * com.yx.yxshop.service
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/30
 * reids封装类,用于封装redis操作
 */
public interface IRedisService {

    /**
     * 保存用户
     */
    void saveUser(String key, User user);

    /**
     * 读取用户
     */
    User loadUser(String key);
}
?IRedisServiceImpl.java
package com.yx.yxshop.service.impl;

import com.yx.yxshop.pojo.User;
import com.yx.yxshop.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

/**
 * com.yx.yxshop.service.impl
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/30
 * 接口实现类
 */
@Service
public class IRedisServiceImpl implements IRedisService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void saveUser(String token, User user) {
        //        将个人信息存放到我们的缓存中
        redisTemplate.opsForValue().set("user:"+token,user);
    }

    @Override
    public User loadUser(String token) {
        return (User) redisTemplate.opsForValue().get("user:"+token);
    }
}
 

? ? ? ? 控制层引用封装类

?

? ? ? ? ? ?在代码当中尽量不要出现"user:",尽量使用常量,我们新建一个类进行定义我们常用的常量。

Constans.java
package com.yx.yxshop.core;

/**
 * com.yx.yxshop.core
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/30
 * 常量类
 */
public class Constans {

    public static final String REDIS_USER_PREFIX = "user:";//缓存前缀类

    public static final String REDIS_CAR_PREFIX = "car:";//购物车常量

    public static final String REDIS_ORDER_PREFIX = "order:";//订单常量



}

? ? ? ? ?在方法中进行引用

? ? ? ? 为了登陆成功在首页显示登陆用户信息的姓名是,我们在后台进行一个Cookie存值。

? ? ? ? ?运行测试

????????

六、自定义验证注解

? ? ? ? 写一个自定义注解类

?IsTrue.java
package com.yx.yxshop.core;

import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * com.yx.yxshop.core
 *
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create 2023/12/30
 * 自定义注解类
 */

@Retention(RetentionPolicy.RUNTIME)//该注解表示项目运行时有效
@Documented//生成文档注解
@Target(ElementType.FIELD)//代表该注解可以用于属性字段
public @interface IsTrue {

    /**
     *
     * 这个字段是否必须
     */
    boolean required() default false;

    /**
     *
     * 字段的正则
     */
    String expr() default "";

//    错误提交信息
    String message() default "{javax.validation.constraints.NotBlank.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};


}

? ? ? ? 我们这个自定义注解类的使用范围比较广,只需要填写两个参数,一个数是否启用,一个是正则。 我们还需要配置一个解析器

?IsTrueConstraintValidator.java
package com.yx.yxshop.core;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.Data;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@Data
public class IsTrueConstraintValidator implements ConstraintValidator<IsTrue, String> {

    private boolean require;
    private String expr;

    @Override
    public void initialize(IsTrue isTrue) {
        expr = isTrue.expr();
        require = isTrue.require();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (!require) return true;
        if (StringUtils.isEmpty(value)) return false;
        return value.matches(expr);
    }

}

? ? ? ? ?有了解析器之后我们还需要在自定义注解类中指定

? ? ? ? ?后面对其进行使用

????????

? ? ? ? 最后运行项目进行登陆测试?


?

?🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路?!🎁

文章来源:https://blog.csdn.net/weixin_74352229/article/details/135278984
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。