在实际业务中,我们如果项目比较小的话,一般都是使用springboot框架做一个单机版本的web项目。此时项目里面会涉及到用户的鉴权,流程图如下:
如上图整个流程如下:
1、客户端传递用户名和密码给服务器端 2、服务器端使用用户名和密码与数据库进行校验 3、校验匹配上代表可以登录成功 4、服务端生成一个md5的token字符串 5、服务端从数据库查询出来当前用户的个人信息 6、服务端使用token作为key,userinfo作为value,使用hash的方式把信息存储到redis中。 7、服务端把token放到responsebody里面,给客户端返回登录成功。
在后面的时候,客户端每一次请求都需要带token过来,然后服务端从redis里面根据token查询用户信息对当前用户进行操作。
试想一下:那么我们每个接口都需要用户的id等信息,那么我们是不是每个接口都要写一遍代码从redis去获取当前的用户token?
答案是不需要的,我们可以写一个拦截器来拦截这个token,然后获取到userinfo之后,把它包装起来,各个接口直接获取userinfo即可。下面我们来试验一下:
1)创建一个springbootweb项目
此处略过
2)定义一个userinfo的类
这个类是从数据库里面查询出来的用户信息,所有的用户基本信息我们都保存到这个类里面来,示例如下:
package org.commom.mapper.user.model;
import java.io.Serializable;
import java.util.Date;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString
@TableName("users")
public class UserDTO implements Serializable{
/**
*
*/
private static final long serialVersionUID = -9062245231148549650L;
@TableId
@JSONField(serialize = false)
private Long id;
@JSONField(serialize = false)
private String username;
private String nickname;
private String head_url;
@JSONField(serialize = false)
private String password;
private Integer ismember;
private Integer member_level;
private Date member_start_time;
private Date member_end_time;
private Date proxy_start_time;
private Date proxy_end_time;
private String invite_code;
@TableLogic
@JSONField(serialize = false)
private Integer del;
}3)创建一个usercontext,
这里我们创建一个usercontext,用来保存用户的信息,同时这里使用threadlocal来表示,示例代码如下:
package org.web.module.context;
import org.commom.mapper.user.model.UserDTO;
public class UserContext {
private static ThreadLocal<UserDTO> userHolder = new ThreadLocal<UserDTO>();
public static void setUser(UserDTO loginUser) {
userHolder.set(loginUser);
}
public static UserDTO getUser() {
return userHolder.get();
}
}4)接着我们创建一个加载类,用来解析用户信息
创建一个LoginUserArgumentResolver的类,用来解析出用户的信息,示例代码如下:
package org.web.module.context;
import org.commom.mapper.user.model.UserDTO;
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;
/**
* 这里统一从requestheader头里面获取token,然后解析出用户信息
* @author daniel.jiang
*
*/
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver{
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> clazz = parameter.getParameterType();
return clazz==UserDTO.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return UserContext.getUser();
}
}5)编写一个拦截器,获取token
这里我们在编写一个拦截器,主要是从request里面获取token信息,然后去数据库查询出userinfo,示例代码如下:
package org.web.module.context;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.commom.mapper.user.model.UserDTO;
import org.commom.model.constants.RedisKeyConstants;
import org.commom.model.exception.LoginException;
import org.commom.model.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import lombok.extern.slf4j.Slf4j;
@SuppressWarnings("deprecation")
@Service
@Slf4j
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler instanceof HandlerMethod) {
UserDTO loginUser = this.getUser(request, response);
UserContext.setUser(loginUser);
}
return super.preHandle(request, response, handler);
}
private UserDTO getUser(HttpServletRequest request, HttpServletResponse response) {
try {
String token = request.getHeader("token");
if (StringUtils.isBlank(token)) {
throw new LoginException();
}
// 然后根据token获取用户登录信息,这里省略获取用户信息的过程
UserDTO user = (UserDTO) redisTemplate.opsForHash()
.get(RedisUtils.getKey(RedisKeyConstants.USER_TOKEN, token), token);
if (null == user) {
throw new LoginException();
}
return user;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new LoginException();
}
}
}在这里我们可以坐拦截,如果获取不到token,并且查询不出来userinfo,那么我们统一抛异常:用户未登录,然后用全局异常进行捕获即可。
6)注册拦截器
最后我们编写一个config,把拦截器给注册进去,示例代码如下:
package org.web.module.config;
import java.util.List;
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.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.web.module.context.LoginInterceptor;
import org.web.module.context.LoginUserArgumentResolver;
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Autowired
private LoginInterceptor loginInterceptor;
@Autowired
private LoginUserArgumentResolver loginUserArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserArgumentResolver);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
.excludePathPatterns("/user/login"); //配置拦截规则
}
}此时我们所有的内容就设置完了,然后当我们接口需要使用到用户信息的时候,只需要添加一个DTO的实体即可,并且不需要任何注释就能获取到当前的用户,示例如下:
public BaseResponse checkin(UserDTO user) {
return userServcie.checkin(user);
}完整的示例图如下:
以上我们就实现了一个拦截token获取对应的用户信息分发给各个接口案例。











还没有评论,来说两句吧...