超详细讲解Java秒杀项目用户验证模块的实现

网友投稿 321 2022-08-19


超详细讲解Java秒杀项目用户验证模块的实现

目录一、用户验证1、在方法内添加请求与反应2、cookie操作的封装3、UserServiceImpl4、跳转界面PathController 二、全局session1、导入依赖2、配置yml文件redis3、开启虚拟机三、自定义redis实现功能1、新建RedisConfig文件2、实现全局session 四、使用参数解析器1、新建WebConfig文件2、定义参数解析器3、PathController4、访问主界面得到相关信息:

接着上期内容超详细讲解java秒杀项目登陆模块的实现

一、用户验证

未登录的用户不能进入首页

根据上期内容,我未登录也能进入首页:

1、在方法内添加请求与反应

①、IUserService

package com.example.seckill.service;

import com.example.seckill.pojo.User;

import com.baomidou.mybatisplus.extension.service.IService;

import com.example.seckill.util.response.ResponseResult;

import com.example.seckill.vo.UserVo;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

*

* 用户信息表 服务类

*

*

* @author lv

* @since 2022-03-15

*/

public interface IUserService extends IService {

ResponseResult> findByAccount(UserVo userVo, HttpServletRequest request, HttpServletResponse response);

}

②、UserServiceImpl

③、UserController

package com.example.seckill.controller;

import com.example.seckill.service.IUserService;

import com.example.seckill.util.response.ResponseResult;

import com.example.seckill.vo.UserVo;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.validation.Valid;

/**

*

* 用户信息表 前端控制器

*

*

* @author lv

* @since 2022-03-15

*/

@RestController

@RequestMapping("/user")

public class UserController {

@Autowired

private IUserService userService;

// 用户登录

@RequestMapping("/login")

public ResponseResult> login(@Valid UserVo userVo, HttpServletRequest request, HttpServletResponse response){

// 调用service的登录验证

return userService.findByAccount(userVo,request,response);

}

}

2、cookie操作的封装

package com.example.seckill.util;

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);

}

/**

* @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 final 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 final 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)) {

// 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;

}

}

3、UserServiceImpl

// 最初版本(session),但Session内数据是服务器共用的

// 所有不让名字重复

String ticket=UUID.randomUUID().toString().replace("-","");

request.getSession().setAttribute(ticket,user);

// 将生成的ticket给用户,用户如何验证?使用cookie

CookieUtils.setCookie(request,response,"ticket",ticket);

点击登录时,查看应用程序,cookie内有值

4、跳转界面PathController

跳转方法:

// 跳所有二级页面

@RequestMapping("/{dir}/{path}")

public String toPath(@PathVariable("dir") String dir, @PathVariable("path") String path, HttpServletRequest request){

String ticket= CookieUtils.getCookieValue(request,"ticket");

if(ticket==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

// 去session中取到ticket对应的用户,判断是否有值

Object obj=request.getSession().getAttribute(ticket);

if(obj==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

return dir+"/"+path;

}

未登录时访问主界面失败:只有登录成功后才能访问

二、全局session

根据以上操作完后,session仍然有问题,,session只能在当前某一台服务器中,

需使用第三者完成数据共享,此处使用redis方式,将需要缓存的数据丢到缓存内

1、导入依赖

此处全局session是spring session中的一个,spring session就是spring中的一个文件

org.apache.commons

commons-pool2

org.springframework.session

spring-session-data-redis

2、配置yml文件redis

spring:redis:host: 47.100.191.44password: xiaoli_redisdatabase: 0port: 6379

3、开启虚拟机

现在开启虚拟机

三、自定义redis实现功能

完成全局session的作用

1、新建RedisConfig文件

用于操作redis

package com.example.seckill.config;

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 {

public RedisTemplate redisTemplate(RedisConnectionFactory factory){

// 新建一个

RedisTemplate redisTemplate=new RedisTemplate();

//设置一下使用连接工厂

redisTemplate.setConnectionFactory(factory);

// 额外设置

// 将key序列化操作,转化为string

redisTemplate.setKeySerializer(new StringRedisSerializer());

// value被转化为json

redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

// 额外设置(hash 就是 map集合)

redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

// 让设置生效

redisTemplate.afterPropertiesSet();

return redisTemplate;

}

}

2、实现全局session

①、写个帮助类,用于调用

IRedisService接口:

package com.example.seckill.service;

import com.example.seckill.pojo.User;

public interface IRedisService {

void putUserByTicket(String ticket, User user);

User getUserByTicket(String ticket);

}

实现接口:

package com.example.seckill.service.impl;

import com.example.seckill.pojo.User;

import com.example.seckill.service.IRedisService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service

public class RedisServiceImpl implements IRedisService {

@Autowired

private RedisTemplate redisTemplate;

@Override

public void putUserByTicket(String ticket, User user) {

redisTemplate.opsForValue().set("user:"+ticket,user,2L, TimeUnit.HOURS);

}

@Override

public User getUserByTicket(String ticket) {

Object obj=redisTemplate.opsForValue().get("user:"+ticket);

if(obj==null || !(obj instanceof User)){

return null;

}

return (User)obj;

}

}

②、redisService到缓存中拿元素

@Autowired

private IRedisService redisService;

// 跳所有二级页面

@RequestMapping("/{dir}/{path}")

public String toPath(@PathVariable("dir") String dir, @PathVariable("path") String path, HttpServletRequest request){

// 获取用户的ticket

String ticket= CookieUtils.getCookieValue(request,"ticket");

if(ticket==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

// 去session中取到ticket对应的用户,判断是否有值

// Object obj=request.getSession().getAttribute(ticket);

// 去缓存中拿元素

User user=redisService.getUserByTicket(ticket);

if(user==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

return dir+"/"+path;

}

③、放到缓存中

UserServiceImpl:

// 放到缓存中去

redisService.putUserByTicket(ticket,user);

package com.example.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;

import com.example.seckill.exception.BusinessException;

import com.example.seckill.pojo.User;

import com.example.seckill.mapper.UserMapper;

import com.example.seckill.service.IRedisService;

import com.example.seckill.service.IUserService;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.example.seckill.util.CookieUtils;

import com.example.seckill.util.MD5Utils;

import com.example.seckill.util.ValidatorUtils;

import com.example.seckill.util.response.ResponseResult;

import com.example.seckill.util.response.ResponseResultCode;

import com.example.seckill.vo.UserVo;

import com.sun.deploy.nativesandbox.comm.Request;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.websocket.Session;

import java.util.Date;

import java.util.UUID;

/**

*

* 用户信息表 服务实现类

*

*

* @author lv

* @since 2022-03-15

*/

@Service

public class UserServiceImpl extends ServiceImpl implements IUserService {

@Autowired

private IRedisService redisService;

@Override

public ResponseResult> findByAccount(UserVo userVo, HttpServletRequest request, HttpServletResponse response) {

// 先判断信息是否符合(账号是否是手机号码,密码是不是空)

// if(!ValidatorUtils.isMobile(userVo.getMobile())){

// throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_MOBLIE);

// }

// if(StringUtils.isBlank(userVo.getPassword())){

// throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);

// }

// 再去数据库查出对应的用户(mobile)

User user=this.getOne(new QueryWrapper().eq("id",userVo.getMobile()));

if(user==null){

throw new BusinessException(ResponseResultCode.USER_ACCOUNT_NOT_FIND);

}

// 比较密码

// 二重加密(前端->后端,后端->数据库)

String salt=user.getSalt();

// 将前台的加密密码和后端的盐再次进行加密

String newPassword=MD5Utils.formPassToDbPass(userVo.getPassword(),salt);

if(!newPassword.equals(user.getPassword())){

throw new BusinessException(ResponseResultCode.USER_PASSWORD_NOT_MATCH);

}

// 修改最后的登录时间

this.update(new UpdateWrapper().eq("id",userVo.getMobile()).set("last_login_date",new Date()).setSql("login_count=login_count+1"));

// 最初版本(session),但Session内数据是服务器共用的

// 所有不让名字重复

String ticket=UUID.randomUUID().toString().replace("-","");

// 放到全局或者当服务器的session

// request.getSession().setAttribute(ticket,user);

// 放到缓存中去

redisService.putUserByTicket(ticket,user);

// 将生成的ticket给用户,用户如何验证?使用cookie

CookieUtils.setCookie(request,response,"ticket",ticket);

return ResponseResult.success();

}

}

④、LocalDateTime无法构造实例

LocalDateTime是Java8推荐使用的时间类,我此处无法转化,需要去配置解析器,我就改变实体类中的LocalDateTime改为时间戳Timestamp

四、使用参数解析器

在controller类中方法内加上User实体类参数,查询出用户,自动将信息赋值

1、新建WebConfig文件

package com.example.seckill.config;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.method.support.HandlerMethodArgumentResolver;

import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration

//打开mvc的功能

@EnableWebMvc

public class WebConfig implements WebMvcConfigurer {

@Autowired

private UserArgumentResolvers userArgumentResolvers;

@Override

public void addArgumentResolvers(List resolvers) {

resolvers.add(userArgumentResolvers);

}

// 使静态资源仍然可使用

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry) {

//静态资源访问映射 映射路径 -> 本地资源路径

registry.addResourceHandler("/static/**")

.addResourceLocations("classpath:/static/");

}

}

2、定义参数解析器

UserArgumentResolvers :

package com.example.seckill.config;

import com.example.seckill.exception.BusinessException;

import com.example.seckill.pojo.User;

import com.example.seckill.service.IRedisService;

import com.example.seckill.util.CookieUtils;

import com.example.seckill.util.response.ResponseResultCode;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.core.MethodParameter;

import org.springframework.stereotype.Component;

import org.springframework.web.bind.support.WebDataBinderFactory;

import org.springframework.web.context.request.NativeWebRequest;

import org.springframework.web.method.support.HandlerMethodArgumentResolver;

import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;

@Component

public class UserArgumentResolvers implements HandlerMethodArgumentResolver {

@Autowired

private IRedisService redisService;

// supportsParameter方法判断参数是否需要解析,决定了resolveArgument方法是否运行

@Override

public boolean supportsParameter(MethodParameter methodParameter) {

return methodParameter.getParameterType() == User.class;

}

// 解析参数

@Override

public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

// 参数解析User,因为很多地方需要做登录验证

HttpServletRequest request=(HttpServletRequest) nativeWebRequest.getNativeRequest();

// 获取用户的ticket

String ticket= CookieUtils.getCookieValue(request,"ticket");

if(ticket==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

// 去session中取到ticket对应的用户,判断是否有值

// Object obj=request.getSession().getAttribute(ticket);

// 去缓存中拿元素

User user=redisService.getUserByTicket(ticket);

if(user==null){

throw new BusinessException(ResponseResultCode.TICKET_ERROR);

}

return user;//经过了参数解析后,参数会变成你这个地方返回的值

}

}

3、PathController

package com.example.seckill.controller;

import com.example.seckill.exception.BusinessException;

import com.example.seckill.pojo.User;

import com.example.seckill.service.IRedisService;

import com.example.seckill.util.CookieUtils;

import com.example.seckill.util.response.ResponseResultCode;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.CookieValue;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller

public class PathController {

// 登录跳首页

@RequestMapping("/")

public String toPath(){

return "login";

}

// 跳所有二级页面

@RequestMapping("/{dir}/{path}")

public String toPath(@PathVariable("dir") String dir, @PathVariable("path") String path, User user){

return dir+"/"+path;

}

}

4、访问主界面得到相关信息:

本期内容结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Springboot如何通过流返回文件
下一篇:springboot实现返回文件流
相关文章

 发表评论

暂时没有评论,来抢沙发吧~