0


2万字带你从0到1搭建一套企业级微服务安全框架

💥《微服务核心技术》专栏已收录,欢迎订阅 💥

文章目录

基于上面Spring Security的几十个章节的学习,想必大家对Spring Security框架已经有了一定的了解。

那么我们开始从零开始搭建一套微服务的安全框架,希望其中的一些思想能给大家一些启发。

技术栈

  • spiring security
  • jwt
  • redis
  • nacos registry
  • spring cloud gateway
  • sentinel
  • nacos config
  • seata
  • mybatis
  • mybatis-plus
  • xxl-job
  • rocketmq

数据交互与实现

说到安全就会涉及认证和授权,那么对什么认证,对什么授权,于是引出如下几张表。

  • 用户表
  • 角色表
  • 权限表

这也是典型的RBAC模型。

所有数据表以及项目源码可以搜公号【

步尔斯特

】回复「1024」即可获得。

在这里插入图片描述
有了数据表,我们来完善具体的代码实现。

数据交互的实现

在这里插入图片描述

部分代码:

packagecom.ossa.system.mapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom.ossa.common.api.bean.User;importorg.springframework.stereotype.Component;@ComponentpublicinterfaceUserMapperextendsBaseMapper<User>{}
packagecom.ossa.system.service;importcom.baomidou.mybatisplus.extension.service.IService;importcom.ossa.common.api.bean.User;publicinterfaceUserServiceextendsIService<User>{}
packagecom.ossa.system.service.impl;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.ossa.common.api.bean.User;importcom.ossa.system.mapper.UserMapper;importcom.ossa.system.service.UserService;importorg.springframework.stereotype.Service;@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsUserService{}

认证设计

通过登录操作完成认证,首先在配置类中应该放过登录的请求,我在这里实现一个匿名注解,会在后面给出代码和解析。

整体的设计思想:通过用户名和密码完成认证,确认用户可信,根据用户信息获取token,每次请求都带上token,完成校验。

  1. 获取传参的用户信息,用户名、密码等。String password = authUser.getPassword();
  2. 将用户名、密码、封装成UsernamePasswordAuthenticationToken对象UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
  3. 获取认证管理器AuthenticationManager authenticationManager = authenticationManagerBuilder.getObject();
  4. 认证Authentication authentication = authenticationManager.authenticate(authenticationToken);
  5. 重写UserDetailsService,从数据库获取用户信息,以完成认证流程。
  6. 认证成功后,根据认证信息生成token
  7. 可将token作为key存入redis,用redis的过期时间代替jwt的token令牌的过期时间
  8. 获取用户身份信息
  9. 将token信息及用户信息返回。

代码实现:

@PostMapping("/login")@AnonymousAccesspublicResponseEntity<Object>login(@Validated@RequestBodyAuthUserDto authUser){// 密码解密//        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());String password = authUser.getPassword();// 将用户名、密码、封装成UsernamePasswordAuthenticationToken对象UsernamePasswordAuthenticationToken authenticationToken =newUsernamePasswordAuthenticationToken(authUser.getUsername(), password);// 获取认证管理器AuthenticationManager authenticationManager = authenticationManagerBuilder.getObject();// 认证核心方法Authentication authentication = authenticationManager.authenticate(authenticationToken);//        // 认证成功之后,将认证信息保存至SecurityContext中//        SecurityContextHolder.getContext().setAuthentication(authentication);// 根据认证信息生成tokenString token = tokenProvider.createToken(authentication);// 获取用户身份信息User one = userService.getOne(newQueryWrapper<User>().eq("username", authUser.getUsername()));UserDto userDto =newUserDto();BeanUtils.copyProperties(one,userDto);

        stringRedisTemplate.opsForValue().set(properties.getOnlineKey()+ token,JSONUtil.toJsonStr(userDto), properties.getTokenValidityInSeconds()/1000,TimeUnit.SECONDS);// 返回 token 与 用户信息Map<String,Object> authInfo =newHashMap<String,Object>(2){{put("token", properties.getTokenStartWith()+ token);put("user", userDto);}};returnResponseEntity.ok(authInfo);}
packagecom.ossa.system.filter;importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;importcom.ossa.common.api.bean.Privilege;importcom.ossa.common.api.bean.Role;importcom.ossa.common.api.bean.User;importcom.ossa.system.mapper.PrivilegeMapper;importcom.ossa.system.mapper.RoleMapper;importcom.ossa.system.service.UserService;importlombok.RequiredArgsConstructor;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;importjavax.persistence.EntityNotFoundException;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@RequiredArgsConstructor@Service("userDetailsService")publicclassUserDetailsServiceImplimplementsUserDetailsService{privatefinalUserService userService;privatefinalRoleMapper roleMapper;privatefinalPrivilegeMapper privilegeMapper ;@OverridepublicUserDetailsloadUserByUsername(String username){User user;org.springframework.security.core.userdetails.User userDetails;try{
            user = userService.getOne(newQueryWrapper<User>().eq("username", username));}catch(EntityNotFoundException e){// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsExceptionthrownewUsernameNotFoundException("", e);}if(user ==null){thrownewUsernameNotFoundException("");}else{List<Role> roles = roleMapper.listByUserId(user.getId());ArrayList<Privilege> privileges =newArrayList<>();

            roles.forEach(role -> privileges.addAll(privilegeMapper.listByRoleId(role.getId())));ArrayList<String> tag =newArrayList<>();

            privileges.forEach(p -> tag.add(p.getTag()));List<SimpleGrantedAuthority> collect = tag.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            userDetails =neworg.springframework.security.core.userdetails.User(username, user.getPassword(), collect);}return userDetails;}}
packagecom.ossa.system.filter;importcn.hutool.core.date.DateField;importcn.hutool.core.date.DateUtil;importcn.hutool.core.util.IdUtil;importcom.ossa.common.bean.SecurityProperties;importio.jsonwebtoken.*;importio.jsonwebtoken.io.Decoders;importio.jsonwebtoken.security.Keys;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.security.authentication.UsernamePasswordAuthenticationToken;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.userdetails.User;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjava.security.Key;importjava.util.ArrayList;importjava.util.Date;importjava.util.concurrent.TimeUnit;@Slf4j@ComponentpublicclassTokenProviderimplementsInitializingBean{privatefinalSecurityProperties properties;privatefinalStringRedisTemplate stringRedisTemplate;publicstaticfinalString AUTHORITIES_KEY ="user";privateJwtParser jwtParser;privateJwtBuilder jwtBuilder;publicTokenProvider(SecurityProperties properties,StringRedisTemplate stringRedisTemplate){this.properties = properties;this.stringRedisTemplate = stringRedisTemplate;}@OverridepublicvoidafterPropertiesSet(){byte[] keyBytes =Decoders.BASE64.decode(properties.getBase64Secret());Key key =Keys.hmacShaKeyFor(keyBytes);
        jwtParser =Jwts.parserBuilder().setSigningKey(key).build();
        jwtBuilder =Jwts.builder().signWith(key,SignatureAlgorithm.HS512);}/**
     * 创建Token 设置永不过期,
     * Token 的时间有效性转到Redis 维护
     *
     * @param authentication /
     * @return /
     */publicStringcreateToken(Authentication authentication){return jwtBuilder
                // 加入ID确保生成的 Token 都不一致.setId(IdUtil.simpleUUID()).claim(AUTHORITIES_KEY, authentication.getName()).setSubject(authentication.getName()).compact();}/**
     * 依据Token 获取鉴权信息
     *
     * @param token /
     * @return /
     */AuthenticationgetAuthentication(String token){Claims claims =getClaims(token);User principal =newUser(claims.getSubject(),"******",newArrayList<>());returnnewUsernamePasswordAuthenticationToken(principal, token,newArrayList<>());}publicClaimsgetClaims(String token){return jwtParser
                .parseClaimsJws(token).getBody();}/**
     * @param token 需要检查的token
     */publicvoidcheckRenewal(String token){// 判断是否续期token,计算token的过期时间Long expire = stringRedisTemplate.getExpire(properties.getOnlineKey()+ token,TimeUnit.SECONDS);long time = expire ==null?0: expire *1000;Date expireDate =DateUtil.offset(newDate(),DateField.MILLISECOND,(int) time);// 判断当前时间与过期时间的时间差long differ = expireDate.getTime()-System.currentTimeMillis();// 如果在续期检查的范围内,则续期if(differ <= properties.getDetect()){long renew = time + properties.getRenew();
            stringRedisTemplate.expire(properties.getOnlineKey()+ token, renew,TimeUnit.MILLISECONDS);}}publicStringgetToken(HttpServletRequest request){finalString requestHeader = request.getHeader(properties.getHeader());if(requestHeader !=null&& requestHeader.startsWith(properties.getTokenStartWith())){return requestHeader.substring(7);}returnnull;}}

授权设计

  1. 设计自己filter,拦截我们生成的token,如果token合法,则将token解析并封装成UsernamePasswordAuthenticationToken,存到安全上下文中
  2. 为了确保授权成功,我们需要将我们的filter放在UsernamePasswordAuthenticationFilter前执行
packagecom.ossa.system.filter;importcn.hutool.core.util.StrUtil;importcom.ossa.common.bean.SecurityProperties;importio.jsonwebtoken.ExpiredJwtException;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.util.StringUtils;importorg.springframework.web.filter.GenericFilterBean;importjavax.servlet.FilterChain;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;publicclassOssaTokenFilterextendsGenericFilterBean{privatestaticfinalLogger log =LoggerFactory.getLogger(OssaTokenFilter.class);privatefinalStringRedisTemplate stringRedisTemplate;privatefinalTokenProvider tokenProvider;privatefinalSecurityProperties properties;/**
     * @param tokenProvider     Token
     * @param properties        JWT
     */publicOssaTokenFilter(TokenProvider tokenProvider,SecurityProperties properties,StringRedisTemplate stringRedisTemplate){this.properties = properties;this.tokenProvider = tokenProvider;this.stringRedisTemplate = stringRedisTemplate;}@OverridepublicvoiddoFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throwsIOException,ServletException{HttpServletRequest httpServletRequest =(HttpServletRequest) servletRequest;String token =resolveToken(httpServletRequest);// 对于 Token 为空的不需要去查 Redisif(StrUtil.isNotBlank(token)){String s =null;try{
                s = stringRedisTemplate.opsForValue().get(properties.getOnlineKey()+ token);}catch(ExpiredJwtException e){
                log.error(e.getMessage());}if(s !=null&&StringUtils.hasText(token)){Authentication authentication = tokenProvider.getAuthentication(token);SecurityContextHolder.getContext().setAuthentication(authentication);// Token 续期
                tokenProvider.checkRenewal(token);}}
        filterChain.doFilter(servletRequest, servletResponse);}/**
     * 初步检测Token
     *
     * @param request /
     * @return /
     */privateStringresolveToken(HttpServletRequest request){String bearerToken = request.getHeader(properties.getHeader());if(StringUtils.hasText(bearerToken)&& bearerToken.startsWith(properties.getTokenStartWith())){// 去掉令牌前缀return bearerToken.replace(properties.getTokenStartWith(),"");}else{
            log.debug("非法Token:{}", bearerToken);}returnnull;}}

核心配置

packagecom.ossa.common.security.core.config;importcom.ossa.common.api.anno.AnonymousAccess;importcom.ossa.common.api.bean.SecurityProperties;importcom.ossa.common.api.enums.RequestMethodEnum;importcom.ossa.common.security.core.filter.OssaTokenFilter;importcom.ossa.common.security.core.filter.TokenProvider;importcom.ossa.common.security.core.handler.JwtAccessDeniedHandler;importcom.ossa.common.security.core.handler.JwtAuthenticationEntryPoint;importlombok.RequiredArgsConstructor;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.http.HttpMethod;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.config.core.GrantedAuthorityDefaults;importorg.springframework.security.config.http.SessionCreationPolicy;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.mvc.method.RequestMappingInfo;importorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;importjava.util.*;@Configuration@EnableWebSecurity@RequiredArgsConstructor@EnableGlobalMethodSecurity(prePostEnabled =true, securedEnabled =true)publicclassOssaSecurityConfigurerextendsWebSecurityConfigurerAdapter{privatefinalTokenProvider tokenProvider;privatefinalSecurityProperties properties;privatefinalApplicationContext applicationContext;privatefinalJwtAuthenticationEntryPoint authenticationErrorHandler;privatefinalJwtAccessDeniedHandler jwtAccessDeniedHandler;privatefinalStringRedisTemplate stringRedisTemplate;@BeanpublicAuthenticationManagerauthenticationManagerBean()throwsException{returnsuper.authenticationManagerBean();}@BeanGrantedAuthorityDefaultsgrantedAuthorityDefaults(){// 去除 ROLE_ 前缀returnnewGrantedAuthorityDefaults("");}@BeanpublicPasswordEncoderpasswordEncoder(){// 密码加密方式returnnewBCryptPasswordEncoder();}@Overrideprotectedvoidconfigure(HttpSecurity httpSecurity)throwsException{OssaTokenFilter customFilter =newOssaTokenFilter(tokenProvider, properties,stringRedisTemplate);// 搜寻匿名标记 url: @AnonymousAccessRequestMappingHandlerMapping requestMappingHandlerMapping =(RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");Map<RequestMappingInfo,HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();// 获取匿名标记Map<String,Set<String>> anonymousUrls =getAnonymousUrl(handlerMethodMap);
        httpSecurity
                // 禁用 CSRF.csrf().disable().addFilterBefore(customFilter,UsernamePasswordAuthenticationFilter.class)// 授权异常.exceptionHandling().authenticationEntryPoint(authenticationErrorHandler).accessDeniedHandler(jwtAccessDeniedHandler)// 防止iframe 造成跨域.and().headers().frameOptions().disable()// 不创建会话.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 静态资源等等.antMatchers(HttpMethod.GET,"/*.html","/**/*.html","/**/*.css","/**/*.js","/webSocket/**").permitAll()// swagger 文档.antMatchers("/swagger-ui.html").permitAll().antMatchers("/swagger-resources/**").permitAll().antMatchers("/webjars/**").permitAll().antMatchers("/*/api-docs").permitAll()// 文件.antMatchers("/avatar/**").permitAll().antMatchers("/file/**").permitAll()// 阿里巴巴 druid.antMatchers("/druid/**").permitAll()// 放行OPTIONS请求.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型// GET.antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(newString[0])).permitAll()// POST.antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(newString[0])).permitAll()// PUT.antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(newString[0])).permitAll()// PATCH.antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(newString[0])).permitAll()// DELETE.antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(newString[0])).permitAll()// 所有类型的接口都放行.antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(newString[0])).permitAll()// 所有请求都需要认证.anyRequest().authenticated();}privateMap<String,Set<String>>getAnonymousUrl(Map<RequestMappingInfo,HandlerMethod> handlerMethodMap){Map<String,Set<String>> anonymousUrls =newHashMap<>(6);Set<String> get =newHashSet<>();Set<String> post =newHashSet<>();Set<String> put =newHashSet<>();Set<String> patch =newHashSet<>();Set<String> delete =newHashSet<>();Set<String> all =newHashSet<>();for(Map.Entry<RequestMappingInfo,HandlerMethod> infoEntry : handlerMethodMap.entrySet()){HandlerMethod handlerMethod = infoEntry.getValue();AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);if(null!= anonymousAccess){List<RequestMethod> requestMethods =newArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());RequestMethodEnum request =RequestMethodEnum.find(requestMethods.size()==0?RequestMethodEnum.ALL.getType(): requestMethods.get(0).name());switch(Objects.requireNonNull(request)){case GET:
                        get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case POST:
                        post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case PUT:
                        put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case PATCH:
                        patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case DELETE:
                        delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;default:
                        all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;}}}
        anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
        anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
        anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
        anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
        anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
        anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);return anonymousUrls;}}

自定义权限注解

packagecom.ossa.common.security.core.config;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.stereotype.Service;importjava.util.Arrays;importjava.util.List;importjava.util.stream.Collectors;@Service(value ="pc")publicclassPermissionConfig{publicBooleancheck(String... permissions){// 获取当前用户的所有权限List<String> permission =SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());// 判断当前用户的所有权限是否包含接口上定义的权限return permission.contains("ADMIN")|| permission.contains("INNER")|| permission.contains("OFFICEIT")||Arrays.stream(permissions).anyMatch(permission::contains);}}

权限异常处理

packagecom.ossa.common.security.core.handler;importorg.springframework.security.access.AccessDeniedException;importorg.springframework.security.web.access.AccessDeniedHandler;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@ComponentpublicclassJwtAccessDeniedHandlerimplementsAccessDeniedHandler{@Overridepublicvoidhandle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException accessDeniedException)throwsIOException{//当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
        response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());}}
packagecom.ossa.common.security.core.handler;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.web.AuthenticationEntryPoint;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@ComponentpublicclassJwtAuthenticationEntryPointimplementsAuthenticationEntryPoint{@Overridepublicvoidcommence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException)throwsIOException{// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException ==null?"Unauthorized": authException.getMessage());}}

网关处理

网关只需要转发token到具体服务即可

在写这篇文章之前,此部分我已经升级成UAA认证授权中心,故没有此处相关代码。

内部流量处理

在内部流量的设计过程中,我们并不需要网关分发的token,故在此设计时,我只在feign的api接口处统一增加权限标识,并经过简单加密。

并在上述的自定的权限注解处放过该标识,不进行权限校验。

packagecom.ossa.feign.config;importcom.ossa.feign.util.EncryptUtil;importfeign.Logger;importfeign.Request;importfeign.RequestInterceptor;importfeign.RequestTemplate;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.context.request.RequestAttributes;importorg.springframework.web.context.request.RequestContextHolder;importorg.springframework.web.context.request.ServletRequestAttributes;importjavax.servlet.http.HttpServletRequest;importjava.util.Enumeration;importjava.util.Objects;importjava.util.concurrent.TimeUnit;/**
 * @author issavior
 *
 * =================================
 *     **
 *      * 修改契约配置,支持Feign原生的注解
 *      * @return 返回 new Contract.Default()
 *      *
 *  &#064;Bean
 *  public Contract feignContract(){
 *      return new Contract.Default();
 *  }
 * ====================================
 */@ConfigurationpublicclassFeignClientConfigimplementsRequestInterceptor{/**
     * 超时时间配置
     *
     * @return Request.Options
     */@BeanpublicRequest.Optionsoptions(){returnnewRequest.Options(5,TimeUnit.SECONDS,5,TimeUnit.SECONDS,true);}/**
     * feign的日志级别
     *
     * @return 日志级别
     */@BeanpublicLogger.LevelfeignLoggerLevel(){returnLogger.Level.FULL;}/**
     * 重写请求拦截器apply方法,循环请求头
     *
     * @param requestTemplate 请求模版
     */@Overridepublicvoidapply(RequestTemplate requestTemplate){RequestAttributes requestAttributes =RequestContextHolder.getRequestAttributes();if(Objects.isNull(requestAttributes)){return;}HttpServletRequest request =((ServletRequestAttributes)(requestAttributes)).getRequest();Enumeration<String> headerNames = request.getHeaderNames();if(headerNames !=null){while(headerNames.hasMoreElements()){String name = headerNames.nextElement();String values = request.getHeader(name);
                requestTemplate.header(name, values);}}Enumeration<String> bodyNames = request.getParameterNames();//        body.append("token").append("=").append(EncryptUtil.encodeUTF8StringBase64("INNER")).append("&");if(bodyNames !=null){while(bodyNames.hasMoreElements()){String name = bodyNames.nextElement();String values = request.getParameter(name);
                requestTemplate.header(name,values);}}

        requestTemplate.header("inner",EncryptUtil.encodeUTF8StringBase64("INNER"));}//    /**//     * 修改契约配置,支持Feign原生的注解//     * @return 返回 new Contract.Default()//     *///    @Bean//    public Contract feignContract(){//        return new Contract.Default();//    }}
热门专栏 欢迎订阅
  1. 《Java系核心技术》
  2. 《中间件核心技术》
  3. 《微服务核心技术》
  4. 《云原生核心技术》

本文转载自: https://blog.csdn.net/CSDN_SAVIOR/article/details/125968635
版权归原作者 步尔斯特 所有, 如有侵权,请联系我们删除。

“2万字带你从0到1搭建一套企业级微服务安全框架”的评论:

还没有评论