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

 🎉🎉欢迎来到我的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);
    }

}

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

         后面对其进行使用

 ​​​​​​​

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


 

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【NLP论文】02 TF-IDF 关键词权值计算

之前写了一篇关于关键词词库构建的文章&#xff0c;没想到反响还不错&#xff0c;最近有空把接下来的两篇补完&#xff0c;也继续使用物流关键词词库举例&#xff0c;本篇文章承接关键词词库构建并以其为基础&#xff0c;将计算各关键词的 TF-IDF 权值&#xff0c;TF-IDF 权值主…

霹雳吧啦Wz《pytorch图像分类》-p2AlexNet网络

《pytorch图像分类》p2AlexNet网络基础及代码 一、零碎知识点1.过拟合2.使用dropout后的正向传播3.正则化regularization4.代码中所用的知识点 二、总体架构分析1.ReLU激活函数2.手算3.模型代码 三、训练花分类课程代码1.model.py2.train.py3.predict.py 一、零碎知识点 1.过拟…

FPGA项目(14)——基于FPGA的数字秒表设计

1.功能设计 设计内容及要求: 1.秒表最大计时范围为99分59. 99秒 2.6位数码管显示&#xff0c;分辨率为0.01秒 3.具有清零、启动计时、暂停及继续计时等功能 4.控制操作按键不超过二个。 2.设计思路 所采用的时钟为50M&#xff0c;先对时钟进行分频&#xff0c;得到100HZ频率…

【Maven】下载配置maven以及IDEA配置maven详情

目录 1、下载maven 2、配置settings.xml 2.1、配置本地仓库 2.2、配置阿里云镜像仓库 2.3、配置JDK 3、配置环境变量 4、IDEA配置maven 1、下载maven maven官网&#xff1a;https://maven.apache.org/ 2、配置settings.xml 2.1、配置本地仓库 <localRepository>C:\…

oracle 9i10g编程艺术-读书笔记1

根据书中提供的下载代码链接地址&#xff0c;从github上找到源代码下载地址。 https://github.com/apress下载好代码后&#xff0c;开始一段新的旅行。 设置 SQL*Plus 的 AUTOTRACE 设置 SQL*Plus 的 AUTOTRACE AUTOTRACE 是 SQL*Plus 中一个工具&#xff0c;可以显示所执行…

GPT4-AIl本地部署-chat AI本地使用

文章目录 GPT4-AIl本地部署GPT4客户端下载地址&#xff1a;对应的下载下载后的文件点击安装&#xff0c;改一下文件存放路径&#xff0c;下面都是默认下一步进度条100%后&#xff0c;点击完成 安装完桌面生成图标&#xff0c;点击选择都是NO&#xff0c;不进行数据上传点击后&a…

Python编程新技能:如何优雅地实现水仙花数?

水仙花数&#xff08;Narcissistic number&#xff09;也被称为阿姆斯特朗数&#xff08;Armstrong number&#xff09;或自恋数等&#xff0c;它是一个非负整数&#xff0c;其特性是该数的每个位上的数字的n次幂之和等于它本身&#xff0c;其中n是该数的位数。简单来说&#x…

一起学Elasticsearch系列-写入原理

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 写入过程写操作写流程写一致性策略 写入原理RefreshMergeFlushTranslog图解写入流程 ES作为一款开源的分布式搜索和分析引擎&#xff0c;以其卓越的性能和灵活的扩…

29 UVM Command Line Processor (CLP)

随着设计和验证环境的复杂性增加&#xff0c;编译时间也增加了&#xff0c;这也影响了验证时间。因此&#xff0c;需要对其进行优化&#xff0c;以便在不强制重新编译的情况下考虑新的配置或参数。我们已经看到了function or task如何基于传递参数进行行为。类似地&#xff0c;…

均方差损失推导

一、损失函数&#xff08;Cost function&#xff09; 定义&#xff1a;用于衡量模型预测结果与真实结果之间差距的函数。&#xff08;有的地方称之为代价函数&#xff0c;但是个人感觉损失函数这个名称更贴近实际用途&#xff09; 理解&#xff1a;&#xff08;以均方差损失函…

Python浪漫520表白代码

系列文章 序号文章目录直达链接表白系列1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏飘字表白代码https://want595.blog.csdn.net/article/details/1349149703无限弹窗表白代码https://want595.blog.csdn.net/article/details/1297945184跳…

使用.Net nanoFramework 驱动ESP32的OLED显示屏

本文介绍如何使用.Net nanoFramework 驱动ESP32的OLED显示屏。我们将会从最基础的部分开始&#xff0c;逐步深入&#xff0c;让你能够理解并实现整个过程。无论你是初学者还是有一定经验的开发者&#xff0c;这篇文章都会对你有所帮助。 1. 硬件准备 1.1 ESP32开发板 这里我们…

搭建flink集群 —— 筑梦之路

Apache Flink 是一个框架和分布式处理引擎&#xff0c; 用于在无边界和有边界数据流上进行有状态的计算。 Flink 能在所有常见集群环境中运行&#xff0c;并能以内存速度和任意规模进行计算。 Flink并没有依靠自身实现所有分布式系统需要解决的问题&#xff0c; 而是在已有集群…

c语言之将输入的十进制转换成二进制数并打印原码反码补码

十进制转二进制 首先&#xff0c;我们要知道的是十进制转换成二进制数的方法。我们一般采用的除二取余的方法&#xff0c;在这里我用32位数组来进行转换。 int main() {printf("请输入一个十进制数\n");int n 0;scanf("%d", &n);int arr[32];int* p…

实战入门 K8s剩下三个模块

1.Label Label是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识&#xff0c;用来对它们进行区分和选择。 Label的特点&#xff1a; 一个Label会以key/value键值对的形式附加到各种对象上&#xff0c;如Node、Pod、Service等等 一个资源对象可以定义任意数量…

VMware虚拟机和Centos7镜像安装

文章目录 安装VMware虚拟机1、下载2、激活 安装Centos7镜像启动虚拟机 安装VMware虚拟机 1、下载 建议还是安装16版本 VMware16下载 https://www.123pan.com/s/HQeA-aX1Sh VMware15 链接&#xff1a;https://pan.baidu.com/s/11UD1hb6IydbxNNPxmh-MqA?pwd0630 提取码&am…

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…

使用YOLOv8和Grad-CAM技术生成图像热图

目录 yolov8导航 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; 概述 环境准备 代码解读 导入库 定义letterbox函数 调整尺寸和比例 计算填充 应用填充 yolov8_heatmap类定义和初始化 后处理函数 绘制检测结果 类的调用函数 热图生成细节 参数解释 we…

QT上位机开发(带配置文件的倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们用qt写过倒计时软件&#xff0c;但是那个时候界面只有分钟和秒钟&#xff0c;这一次我们希望在之前的基础上拓展一下。第一&#xff0c;可…

ThreadLocal 是什么?它的实现原理是什么?

文章目录 ThreadLocal 是什么&#xff1f;它的实现原理是什么&#xff1f; ThreadLocal 是什么&#xff1f;它的实现原理是什么&#xff1f; ThreadLocal 是一种线程隔离机制&#xff0c;它提供了多线程环境下对于共享变量访问的安全性。 在多线程访问共享变量的场景中&#…
最新文章