微服务拦截器处理跨域验证token

作者: Ian | 2019-02-05 | 阅读
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.i78dk.easyloan.web.common.interceptor.AuthorizationInterceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import java.io.IOException;
import java.util.List;
import java.util.Date;

@Configuration
public class WebAppConfiguration extends WebMvcConfigurerAdapter {

    @Autowired
    private AuthorizationInterceptor authorizationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 自定义拦截器,添加拦截路径和排除拦截路径
        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/78dk/web/**")
        .excludePathPatterns("/swagger-ui.html");
    }

//    @Override
//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//        converter.setFeatures(SerializerFeature.WriteNullListAsEmpty,
//                SerializerFeature.WriteMapNullValue,
//                //字符串null返回空字符串
//                SerializerFeature.WriteNullStringAsEmpty,
//                SerializerFeature.WriteNullBooleanAsFalse,
//                SerializerFeature.WriteNullNumberAsZero,
//                SerializerFeature.PrettyFormat);
//        converters.add(converter);
//    }
    
//    @Bean
//    public HttpMessageConverters fastJsonHttpMessageConverters() {
//        //1、创建FastJson信息转换对象 
//        FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();
//        //2、创建FastJsonConfig对象并设定序列化规则  序列化规则详见SerializerFeature类中,后面会讲
//        FastJsonConfig fastJsonConfig= new FastJsonConfig();
//        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,SerializerFeature.WriteNonStringKeyAsString);
//        //本人就坑在WriteNonStringKeyAsString 将不是String类型的key转换成String类型,否则前台无法将Json字符串转换成Json对象
//        
//        //如果值为null,则返回空串
//        ValueFilter dateFilter = (Object var1, String var2, Object var3) -> {
//            try {
//                if (var3 == null && var1 != null && Date.class.isAssignableFrom(var1.getClass().getDeclaredField(var2).getType())){
//                    return "";
//                }
//            } catch (Exception e) {}
//            return var3;
//        };
//        fastJsonConfig.setSerializeFilters(dateFilter);
//        
//        //4、将转换规则应用于转换对象 
//        fastConverter.setFastJsonConfig(fastJsonConfig);
//        return new HttpMessageConverters(fastConverter);
//    }
    
    @Bean
    public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
//        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
        // Include.Include.ALWAYS 默认
        // Include.NON_DEFAULT 属性为默认值不序列化
        // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
        // Include.NON_NULL 属性为NULL 不序列化
        //objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 字段保留,将null值转为""
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                jsonGenerator.writeString("");
            }
        });
        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);

        return mappingJackson2HttpMessageConverter;
    }


//    @Bean
//    public FastJsonHttpMessageConverter fastJsonHttpMessageConverter(){
//        return new FastJsonHttpMessageConverter();
//    }

    //配置解析器
    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/**");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }


    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        //配置静态资源处理
        configurer.enable();
    }



    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        super.addResourceHandlers(registry);
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }



    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        return corsConfiguration;
    }

    /**
     * 跨域过滤器
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 4
        return new CorsFilter(source);
    }


}


//////////////////////////////////
import com.alibaba.fastjson.JSONObject;
import com.i78dk.easyloan.domain.system.dto.AdminRoleOrgDto;
import com.i78dk.easyloan.web.annotation.AuthToken;
import com.i78dk.easyloan.web.common.constants.ResultCodeConstants;
import com.i78dk.easyloan.web.common.context.UserContext;
import com.i78dk.easyloan.web.common.redis.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;


/**
 * @author zhangqi@78dk.com
 * @create: 2018-11-16 15:17
 */
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    @Autowired
    RedisService redisService;
    private Logger log = LoggerFactory.getLogger(AuthorizationInterceptor.class);
    /**
     * 存放鉴权信息的Header名称,默认是AuthorToken
     */
    private String httpHeaderName = "AuthorToken";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {


    	
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 如果打上了AuthToken注解则不需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
            return true;
        } else {
            String token = request.getHeader(httpHeaderName);
            log.info("token is {}", token);

            if (null != token && redisService.exists(token)) {
                //登录数据初始化
                AdminRoleOrgDto sysAdmin = (AdminRoleOrgDto) redisService.get(token);
                UserContext.set(sysAdmin);
                return true;
            } else {
                JSONObject jsonObject = new JSONObject();
                PrintWriter out = null;
                try {
                	response.setContentType("application/json;charset=UTF-8");
                    jsonObject.put("code", ResultCodeConstants.CODE_S0014);
                    jsonObject.put("data", "");
                    jsonObject.put("msg", ResultCodeConstants.getMsg(ResultCodeConstants.CODE_S0014));
                    out = response.getWriter();
                    out.println(jsonObject);
                    return false;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != out) {
                        out.flush();
                        out.close();
                    }
                }
            }
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //执行完成清理当前线程
        UserContext.clean();
    }

}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author zhangqi@78dk.com
 * @description: 加上该注解的类http请求时需要进行token鉴权
 * @create: 2018-11-16 15:17
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {

}

/////
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

/**
 * @author zhangqi@78dk.com
 * @create: 2018-11-16 15:17
 */
@Component
public class Md5TokenGenerator implements TokenGenerator {

    @Override
    public String generate(String... strings) {
        long   timestamp = System.currentTimeMillis();
        String tokenMeta = "";
        for (String s : strings) {
            tokenMeta = tokenMeta + s;
        }
        tokenMeta = tokenMeta + timestamp;
        String token = DigestUtils.md5DigestAsHex(tokenMeta.getBytes());
        return token;
    }
}

@Component
public interface TokenGenerator {
    public String generate(String... strings);

}

/**
 登录用户信息上下文 
 */
public class UserContext {
	
	private static ThreadLocal<AdminRoleOrgDto> UserContext = new ThreadLocal<AdminRoleOrgDto>();
	
	public static AdminRoleOrgDto get(){
		if(null == UserContext.get()){
			UserContext.set(new AdminRoleOrgDto());
		}
		
		return UserContext.get();
	}
	
	public static void set(AdminRoleOrgDto user){
		UserContext.set(user);
	}
	
	public static void clean(){
		UserContext.remove();
	}
}

使用注解可参考: 跨域问题 springboot可参考:详解WebMvcConfigurer接口

文章中若有不足之处,还望指出。坚持是一种精神,分享是一种快乐!


版权声明:本文由 Ian 在 2019年02月05日发表。本文采用CC BY-NC-SA 4.0许可协议,非商业转载请注明出处,不得用于商业目的。
文章题目及链接:《微服务拦截器处理跨域验证token》




  相关文章:


留言区:

TOP