在实际业务中,我们如果项目比较小的话,一般都是使用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获取对应的用户信息分发给各个接口案例。
还没有评论,来说两句吧...