spring boot使用自定义参数解析器

需求:在用户已经登录后请求别的接口时注入用户对象

 

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也能正常登录");
    }

}

 

 

posted @ 2019-07-13 09:15  渣男梦想  阅读(3792)  评论(0编辑  收藏  举报