▶【SecKill】U2 实现登录功能
▶【SecKill】U2 实现登录功能
一、数据库设计
1、新建数据库表miaosha_user`
CREATE TABLE `miaosha_user` (
`id` bigint(20) NOT NULL COMMENT '用户ID、手机号码',
`nickname` varchar(255) NOT NULL,
`password` varchar(32) DEFAULT NULL COMMENT 'MD5(MD5(pass明文+固定salt)+salt)',
`salt` varchar(10) DEFAULT NULL,
`head` varchar(128) DEFAULT NULL COMMENT '头像、云存储ID',
`register_date` datetime DEFAULT NULL COMMENT '注册时间',
`last_login_date` datetime DEFAULT NULL COMMENT '上次登录时间',
`login_count` int(11) DEFAULT '0' COMMENT '登录次数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
【注】字符集采用的是utf8mb4(most bytes 4)。简单来说,utf8mb4是utf8的超集,能够用4个字节存储更多的字符。标准UTF-8字符集编码可以用1~4个字节取编码21位字符,但是在MySQL中,utf8最多使用3个字节,像一些表情emoji和不常用的字符如“墅”需要用4个字节才能表示出来。用utf8mb4能解决以上问题。数据库中存储了"动态"salt值
二、明文密码两次MD5处理:安全
1、为什么密码要用两次MD5?
2、在pom.xml添加MD5依赖
<!-- 8.添加MD5依赖 --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.6</version> </dependency>
3、新建com.kirin.miaosha.util包,作为MD5工具类
4、新建MD5Util工具类
(1)(测试)做一次MD5
package com.kirin.miaosha.util;
public class MD5Util {
public static String md5(String src) {
return DigestUtils.md5Hex(src); //调用DigestUtils,实现md5处理
}
private static final String salt = "1a2b3c4d";
public static String inputPassToFormPass(String inputPass) {
String str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
System.out.println(str);
return md5(str); //做一次MD5
}
public static void main(String[] args) {
System.out.println(inputPassToFormPass("123456"));
}
}
(2)★(完整)做2次MD5
(3)做2次MD5后,得到加密密码b7797cce01b4b131b433b6acf4add449,将其输入数据库表中作为一条记录
5、新建LoginController类,直接复制DemoController.java
6、新建登录页面
(1)在 src/main/resources/templates 中新建login.html
(2)导入静态CSS模板
(3)login.html:
<!DOCTYPE HTML>
<!-- 定义一个th -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登录</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- jquery -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<!-- bootstrap -->
<link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" />
<script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script>
<!-- jquery-validator -->
<script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script>
<script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script>
<!-- layer -->
<script type="text/javascript" th:src="@{/layer/layer.js}"></script>
<!-- md5.js -->
<script type="text/javascript" th:src="@{/js/md5.min.js}"></script>
<!-- common.js -->
<script type="text/javascript" th:src="@{/js/common.js}"></script>
</head>
<body>
<form name="loginForm" id="loginForm" method="post" style="width:50%; margin:0 auto">
<h2 style="text-align:center; margin-bottom: 20px">用户登录</h2>
<div class="form-group">
<div class="row">
<label class="form-label col-md-4">手机号码(11位):</label>
<div class="col-md-5">
<input id="mobile" name = "mobile" class="form-control" type="text" placeholder="手机号码" required="true" minlength="11" maxlength="11" />
</div>
<div class="col-md-1">
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<label class="form-label col-md-4">密码:</label>
<div class="col-md-5">
<input id="password" name="password" class="form-control" type="password" placeholder="密码" required="true" minlength="6" maxlength="16" />
</div>
</div>
</div>
<div class="row">
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="reset" onclick="reset()">重置</button>
</div>
<div class="col-md-5">
<button class="btn btn-primary btn-block" type="submit" onclick="login()">登录</button>
</div>
</div>
</form>
</body>
<script>
//登录
function login(){
$("#loginForm").validate({ //参数校验:取Form表单的id=loginForm
submitHandler:function(form){ //若验证通过,则回调doLogin()方法
doLogin();
}
});
}
function doLogin(){
g_showLoading();
var inputPass = $("#password").val(); //获取用户输入的明文密码
var salt = g_passsword_salt; //静态的salt,用于第一次MD5,在输入的密码后进行拼接(common.js)
var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4); //转换、拼接
var password = md5(str); //做一次MD5
//提交
$.ajax({
url: "/login/do_login",
type: "POST",
data:{
mobile:$("#mobile").val(), //获取用户输入的手机号
password: password //计算2次MD5后的加密密码
},
//提交成功后的回调方法
success:function(data){ //成功后会返回data
layer.closeAll();
//console.log(data);
if(data.code == 0){
layer.msg("成功");
window.location.href="/goods/to_list";
}else{
layer.msg(data.msg);
}
},
//提交失败后的回调方法
error:function(){
layer.closeAll(); //关闭Loading...框
}
});
}
</script>
</html>
7、新建com.kirin.miaosha.vo包:接收参数
新建LoginVo.java:接收参数
LoginController.java:
LoginController.java:参数校验
CodeMsg.java:设置登录模块的报错弹窗
//登录模块 5002XX
public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");
在com.kirin.miaosha.util包中,新建ValidatorUtil.java,用于判断用户登录时手机号的格式
package com.kirin.miaosha.util;
//用于判断用户登录时输入的格式
public class ValidatorUtil {
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}"); //以1开头,后跟10个数字
public static boolean isMobile(String src) {
if(StringUtils.isEmpty(src)) {
return false;
}
Matcher m = mobile_pattern.matcher(src);
return m.matches();
}
}
8、在com.kirin.miaosha.domain包中,新建MiaoshaUser.java,对应miaosha_user数据库表
package com.kirin.miaosha.domain;
import java.util.Date;
public class MiaoshaUser {
private Long id;
private String nickname;
private String password;
private String salt;
private String head;
private Date registerDate;
private Date lastLoginDate;
private Integer loginCount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public Date getRegisterDate() {
return registerDate;
}
public void setRegisterDate(Date registerDate) {
this.registerDate = registerDate;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public Integer getLoginCount() {
return loginCount;
}
public void setLoginCount(Integer loginCount) {
this.loginCount = loginCount;
}
}
9、在com.kirin.miaosha.dao包中,新建MiaoshaUserDao.java,对应MiaoshaUser.java的接口方法
package com.kirin.miaosha.dao;
@Mapper
public interface MiaoshaUserDao {
@Select("select * from miaosha_user where id = #{id}")
public MiaoshaUser getById(@Param("id")long id);
}
10、在com.kirin.miaosha.service包中,新建MiaoshaUserService.java,对应MiaoshaUser.java的接口方法
8、完整代码:
com.kirin.miaosha.util / MD5Util.java:
package com.kirin.miaosha.util;
public class MD5Util {
//做一次MD5
public static String md5(String src) {
return DigestUtils.md5Hex(src); //调用DigestUtils,实现md5处理
}
//1.做第一次MD5
//静态的salt,用于第一次MD5,在输入的密码后进行拼接
private static final String salt = "1a2b3c4d";
//1.做第一次MD5:把用户输入的明文密码转换成Form表单格式
public static String inputPassToFormPass(String inputPass) {
//拼接字符时没有添加"",出现了登录验证失败的问题
String str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
System.out.println(str);
return md5(str); //做一次MD5
}
//2.做第二次MD5:把Form表单格式的密码转换成DB格式的密码
public static String formPassToDBPass(String formPass, String salt) { //动态的salt,取随机值
String str = ""+salt.charAt(0)+salt.charAt(2) + formPass +salt.charAt(5) + salt.charAt(4);
return md5(str); //做一次MD5
}
//3.合并第一和第二次转换(MD5):把用户输入的明文密码转换成DB格式的密码
public static String inputPassToDbPass(String inputPass, String saltDB) {
String formPass = inputPassToFormPass(inputPass);
String dbPass = formPassToDBPass(formPass, saltDB);
return dbPass;
}
//编写主类进行测试
public static void main(String[] args) {
//1.做第一次MD5
//System.out.println(inputPassToFormPass("123456")); //输出:d3b1294a61a07da9b49b6e22b2cbd7f9 ——> 破解后12123456c3
//2.做第二次MD5
//System.out.println(formPassToDBPass(inputPassToFormPass("123456"), "1a2b3c4d")); //设定动态salt的值为1a2b3c4d
System.out.println(inputPassToDbPass("123456", "1a2b3c4d")); //b7797cce01b4b131b433b6acf4add449
}
}
com.kirin.miaosha.util / ValidatorUtil.java:
package com.kirin.miaosha.util;
//用于判断用户登录时输入的格式
public class ValidatorUtil {
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}"); //以1开头,后跟10个数字
public static boolean isMobile(String src) {
if(StringUtils.isEmpty(src)) {
return false;
}
Matcher m = mobile_pattern.matcher(src);
return m.matches();
}
}
com.kirin.miaosha.vo / LoginVo.java:
package com.imooc.miaosha.vo;
public class LoginVo {
private String mobile;private String password;
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
}
}
com.kirin.miaosha.domain / MiaoshaUser.java:
package com.kirin.miaosha.domain;
public class MiaoshaUser {
private Long id;
private String nickname;
private String password;
private String salt;
private String head;
private Date registerDate;
private Date lastLoginDate;
private Integer loginCount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public Date getRegisterDate() {
return registerDate;
}
public void setRegisterDate(Date registerDate) {
this.registerDate = registerDate;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public Integer getLoginCount() {
return loginCount;
}
public void setLoginCount(Integer loginCount) {
this.loginCount = loginCount;
}
}
com.kirin.miaosha.service / MiaoshaUserService.java:
package com.kirin.miaosha.service;
@Service
public class MiaoshaUserService {
@Autowired
MiaoshaUserDao miaoshaUserDao;
public MiaoshaUser getById(long id) {
return miaoshaUserDao.getById(id);
}
public CodeMsg login(LoginVo loginVo) {
if(loginVo == null) {
return CodeMsg.SERVER_ERROR;
}
String mobile = loginVo.getMobile();
String formPass = loginVo.getPassword();
//判断手机号是否存在
MiaoshaUser user = getById(Long.parseLong(mobile));
if(user == null) {
return CodeMsg.MOBILE_NOT_EXIST;
}
//验证密码
String dbPass = user.getPassword(); //获取数据库中的密码
String saltDB = user.getSalt(); //获取数据库中的静态salt
String calcPass = MD5Util.formPassToDBPass(formPass, saltDB); //做2次MD5
if(!calcPass.equals(dbPass)) { //判断计算出来的MD5加密密码和数据库中的密码是否一致
return CodeMsg.PASSWORD_ERROR;
}
return CodeMsg.SUCCESS;
}
}
com.kirin.miaosha.controller / LoginController.java:
package com.kirin.miaosha.controller;
@Controller
@RequestMapping("/login")
public class LoginController {
private static Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
MiaoshaUserService userService;
@Autowired
RedisService redisService;
@RequestMapping("/to_login")
public String toLogin() {
return "login"; //返回页面
}
@RequestMapping("/do_login")
@ResponseBody
public Result<Boolean> doLogin(LoginVo loginVo) {
log.info(loginVo.toString()); //在LoginVo.java中生成toString()
//参数校验
String passInput = loginVo.getPassword();
String mobile = loginVo.getMobile();
if(StringUtils.isEmpty(passInput)) {
return Result.error(CodeMsg.PASSWORD_EMPTY);
}
if(StringUtils.isEmpty(mobile)) {
return Result.error(CodeMsg.MOBILE_EMPTY);
}
if(!ValidatorUtil.isMobile(mobile)) { //验证mobile的输入的格式,是否是以1开头,后跟10个数字
return Result.error(CodeMsg.MOBILE_ERROR);
}
//登录
CodeMsg cm = userService.login(loginVo);
if(cm.getCode() == 0) {
return Result.success(true);
}else {
return Result.error(cm);
}
}
}
三、JSR303参数检验+全局异常处理器
1、添加JSR参数校验依赖
<!-- 9.添加JSR参数校验依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
2、
① com.kirin.miaosha.controller / LoginController.java:
② 新建com.kirin.miaosha.validator包
新建IsMobile类,作为自定义验证器
package com.kirin.miaosha.validator;
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) //能够标注的范围
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class }) //这个注解帮助我们处理逻辑,其中IsMobileValidator.class是真正处理逻辑的类
public @interface IsMobile { //根据已有注解@NotNull,仿写而来
boolean required() default true;
String message() default "手机号码格式错误"; //添加错误信息
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
新建IsMobileValidator类
先看类的声明部分,public class IsMobileValidator implements ConstraintValidator<IsMobile, String>,它有两个泛型,第一个是自定义的注解类,第二个是要验证的参数类型,另外实现该接口的逻辑类,被spring管理成bean,可以在需要的地方进行装配
其中有一个initialize,初始化方法,它调用的是我们自定义注解中写的required()方法,默认需要有值
另一个方法isValid,则对逻辑进行验证,true验证通过,false验证失败
package com.kirin.miaosha.validator;
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {
private boolean required = false;
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if(required) { //在必须有值的情况下
return ValidatorUtil.isMobile(value);
}else {
if(StringUtils.isEmpty(value)) { //在不要求有值的情况下
return true; //空值是允许的
}else { //有值就给它判断
return ValidatorUtil.isMobile(value);
}
}
}
}
3、全局异常处理器
(1)新建com.kirin.miaosha.exception包
(2)新建GlobalExceptionHandler类
在CodeMsg.java中,添加一条异常定义
package com.kirin.miaosha.result;
public class CodeMsg {
private int code;
private String msg;
//定义通用异常
public static CodeMsg SUCCESS = new CodeMsg(0, "success");
public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常");
public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s"); //带参数
//登录模块 5002XX
public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");
//商品模块5003XX
//订单模块5004XX
//秒杀模块5005XX
private CodeMsg( ) {
}
private CodeMsg( int code,String msg ) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
//定义带参数的CodeMsg
public CodeMsg fillArgs(Object... args) {
int code = this.code;
String message = String.format(this.msg, args); //把原始的message拼接上参数
return new CodeMsg(code, message);
}
@Override
public String toString() {
return "CodeMsg [code=" + code + ", msg=" + msg + "]";
}
}
其中String.format()能够根据传入的字符串格式,比如"参数校验异常:%s",其中%s,能被第二个传入的参数进行替换,从而形成动态的字符串
GlobalExceptionHandler.java:
package com.kirin.miaosha.exception;
//全局异常处理器:处理登录异常没有显示在页面上的问题
@ControllerAdvice //它是增强的Controller,能够实现全局异常处理和全局数据绑定
@ResponseBody
public class GlobalExceptionHandler {
//配合@ExceptionHandler(value = Exception.class),它能够实现对所有异常的接受,而在方法中,对不同的异常进行处理
@ExceptionHandler(value=Exception.class) //拦截所有的异常
public Result<String> exceptionHandler(HttpServletRequest request, Exception e){
if(e instanceof BindException) {
BindException ex = (BindException)e; //获取错误列表,拿取其中的第一个
List<ObjectError> errors = ex.getAllErrors();
ObjectError error = errors.get(0);
String msg = error.getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
}else {
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
运行:出现提示框,则成功!
(3)自动新建GlobalException类:全局异常
修改com.kirin.miaosha.service / MiaoshaUserService.java代码:
修改com.kirin.miaosha.exception / GlobalExceptionHandler.java代码:
修改com.kirin.miaosha.controller / LoginController.java代码:
package com.kirin.miaosha.controller;
@Controller
@RequestMapping("/login")
public class LoginController {
private static Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
MiaoshaUserService userService;
@Autowired
RedisService redisService;
@RequestMapping("/to_login")
public String toLogin() {
return "login"; //返回页面
}
@RequestMapping("/do_login")
@ResponseBody
// public Result<Boolean> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
public Result<Boolean> doLogin(@Valid LoginVo loginVo) { //2.JSR303参数检验
log.info(loginVo.toString()); //在LoginVo.java中生成toString()
//1.自己写的参数校验
// String passInput = loginVo.getPassword();
// String mobile = loginVo.getMobile();
// if(StringUtils.isEmpty(passInput)) {
// return Result.error(CodeMsg.PASSWORD_EMPTY);
// }
// if(StringUtils.isEmpty(mobile)) {
// return Result.error(CodeMsg.MOBILE_EMPTY);
// }
// if(!ValidatorUtil.isMobile(mobile)) { //验证mobile的输入的格式,是否是以1开头,后跟10个数字
// return Result.error(CodeMsg.MOBILE_ERROR);
// }
//登录
userService.login(loginVo);
return Result.success(true);
}
}
四、分布式Session
(1)com.kirin.miaosha.service / MiaoshaUserService.java:登录成功后,生成cookie
封装addCookie():
private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) {
redisService.set(MiaoshaUserKey.token, token, user);
Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token);
cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
(2)在com.kirin.miaosha.util包中,新建UUIDUtil.java,UUID的工具类
package com.kirin.miaosha.util;
import java.util.UUID;
public class UUIDUtil {
public static String uuid() {
return UUID.randomUUID().toString().replace("-", "");
}
}
(3)新建商品列表页goods_list.html
(4)在com.kirin.miaosha.controller包中,新建GoodsController.java,使用户登录后跳转到商品列表页goods_list.html
package com.kirin.miaosha.controller;
@Controller
@RequestMapping("/goods")
public class GoodsController {
@Autowired
MiaoshaUserService userService;
@Autowired
RedisService redisService;
@RequestMapping("/to_list")
public String toList(HttpServletResponse response,Model model,
@CookieValue(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String cookieToken, //根据参数value在Cookie中获取值
@RequestParam(value = MiaoshaUserService.COOKI_NAME_TOKEN,required = false) String paramToken){ //让我们在Request中能获取参数,解决的主要是,移动手机端不使用Cookie存值的问题
//参数校验
if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)){
return "login";
}
String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
MiaoshaUser user = userService.getByToken(response,token);
model.addAttribute("user",user);
return "goods_list";
}
}
(5)在(4)中,每次获取参数都要写这么一大段,简化方法:
@RequestMapping("/to_list") //跳转到商品列表页goods_list.html
public String toList(HttpServletResponse response,Model model,MiaoshaUser user){
model.addAttribute("user",user);
return "goods_list";
}
【原理】WebMvcConfigurerAdapter
采用的是继承WebMvcConfigurerAdapter,重写其中addArgumentResolvers()方法,该方法实现的是参数解析的功能
【解决方法】
新建com.kirin.miaosha.config包,新建WebConfig.java:
package com.kirin.miaosha.config;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{
@Autowired
UserArgumentResolver userArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
}
新建UserArgumentResolver.java:
package com.kirin.miaosha.config;
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
//实现HandlerMethodArgumentResolver接口,必须重写其中的两个方法,supportsParameter()和resolveArgument()
@Autowired
MiaoshaUserService userService;
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz==MiaoshaUser.class;
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
return null;
}
String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
return userService.getByToken(response, token);
}
private String getCookieValue(HttpServletRequest request, String cookiName) {
Cookie[] cookies = request.getCookies();
for(Cookie cookie : cookies) {
if(cookie.getName().equals(cookiName)) {
return cookie.getValue();
}
}
return null;
}
}
实现HandlerMethodArgumentResolver接口,必须重写其中的两个方法,supportsParameter()和resolveArgument()