需求:在用户已经登录后请求别的接口时注入用户对象
1、自定义需要拦截的参数注解和用户实体对象
package io.xiongdi.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 登录用户注解 * @author wujiaxing * @date 2019-06-30 */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface LoginUser { }
package io.xiongdi.entity; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.time.LocalDateTime; /** * 用户实体类 * @author wujiaxing */ @Data @Builder @TableName("tb_user") public class UserEntity implements Serializable { private static final long serialVersionUID = 1315432620351507739L; public UserEntity(){} public UserEntity(long userId, String username, String mobile, String password, LocalDateTime createTime) { this.userId = userId; this.username = username; this.mobile = mobile; this.password = password; this.createTime = createTime; } /** * 用户ID */ @TableId private long userId; /** * 用户名 */
private String username; /** * 电话号码 */
private String mobile; /** * 密码 */ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private String password; /** * 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime; }
2、自定义参数解析器类,实现 HandlerMethodArgumentResolver 接口,并实现其方法
package io.xiongdi.resolver; import io.xiongdi.annotation.LoginUser; import io.xiongdi.entity.UserEntity; import io.xiongdi.interceptor.AuthorizationInterceptor; import io.xiongdi.service.UserService; 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.context.request.RequestAttributes; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** * 方法参数解析器接口,这个接口是SpringMVC参数解析绑定的核心接口。 * 不同的参数类型绑定都是通过实现这个接口来实现。 * 也可以通过实现这个接口来自定义参数解析器 * @author wujiaxing * @date 2019-06-30 */ @Component public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Autowired private UserService userService; /** * 该解析器是否支持parameter参数的解析 * @param parameter 拦截到的参数 * @return 是否符合我们的拦截规则 * <p> * isAssignableFrom方法作用是判断参数类型是否为UserEntity类或是父类或是父接口 * </p> */ @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class); } /** * <p> * 将方法参数从给定请求(webRequest)解析为参数值并返回 * 请求顺序: * 1.进入拦截器,拦截有token请求头的用户,说明是验证过的 * 2.进入自定义参数解析器supportsParameter方法,解析带有LoginUser注解并且类UserEntity或父类的参数 * 3.进入自定义参数解析器resolveArgument方法,将存在作用域里的userid拿出来查询到user用户信息,赋给UserEntity参数 * </p> * @param parameter * @param mavContainer * @param webRequest * @param binderFactory * @return * @throws Exception */ @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { // 从请求作用域中获取userid
Object o = webRequest.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST); if (o == null) { return null; } UserEntity userEntity = userService.getById((long) o); return userEntity; } }
3、将自定义的参数解析器添加到 spring boot
package io.xiongdi.config; import io.xiongdi.interceptor.AuthorizationInterceptor; import io.xiongdi.resolver.LoginUserHandlerMethodArgumentResolver; 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.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; /** * @author wujiaxing * <p> * 此配置类可配置拦截器、参数解析器、返回值解析器、跨域支持等等 * </p> */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private AuthorizationInterceptor authorizationInterceptor; @Autowired private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver; /** * 拦截器配置 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**"); } /** * 跨域支持配置 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowCredentials(true).allowedOrigins("*").allowedMethods("GET", "PUT", "DELETE", "POST", "OPTIONS").maxAge(3600); } /** * 参数解析配置 * @param resolvers */ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(loginUserHandlerMethodArgumentResolver); } }
4、配置已经好了,可以写一个接口测试一下了
package io.xiongdi.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.xiongdi.annotation.Login; import io.xiongdi.annotation.LoginUser; import io.xiongdi.common.utils.R; import io.xiongdi.entity.UserEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author wujiaxing * @date 2019-07-07 */ @Api(tags = "测试接口") @RestController @RequestMapping("/api") public class ApiTestController { // 测试用这个方法就行,下面两个方法是别的功能的,@Login 注解也是自定义的,测试时可以去掉09:11:38 @Login @ApiOperation(value = "获取用户对象", response = UserEntity.class) @GetMapping("userInfo") public R userInfo(@LoginUser UserEntity userEntity) { return R.ok().put("user", userEntity); } @Login @ApiOperation("获取用户ID") @GetMapping("userId") public R userId(@RequestAttribute("userId") long userId) { return R.ok().put("userId", userId); } @ApiOperation("忽略token测试") @GetMapping("notToken") public R notToken() { return R.ok().put("msg", "无需token也能正常登录"); } }