Spring Cloud Advanced Road | 14: Services Gateway reconstruction (FactoryBean, dynamic configuration, token parsing, user pass)

Foreword

 

Speaking in front of the services gateway Spring Cloud Advanced Road | Seven: Serving Gateway (Zuul) , Spring Cloud Advanced Road | X: Security Services Gateway Integration Framework (Zuul + Spring Cloud Oauth2) , in which the code was chaotic.

Furthermore, the dynamic configuration can not be achieved related attributes, pre-authentication filters to create and run coupled logic depth, as subsequent addition of other content, inevitably difficult to expand.

So, today, on these issues, the reconstruction of the gateway code, in order to achieve the creation, operation separate, related properties dynamically configurable.

 

Ready to work

Multiplexing Spring Cloud Advanced Road | X: Security Services Gateway Integration Framework (zuul + Spring Cloud Oauth2) gateway project xmall-zuul, product engineering xmall-product, authorized engineering xmall-auth article.

 

Endpoint Management Oauth2

Creating Oauth2 endpoint constant interface.

package com.luas.xmall.gateway.oauth2.endpoint;
public interface AuthorizationServerEndpoints {
    String AUTHORIZATION_ENDPOINT = "/oauth/authorize";
    String TOKEN_ENDPOINT = "/oauth/token";
    String CHECK_TOKEN_ENDPOINT = "/oauth/check_token";
    String TOKEN_KEY_ENDPOINT = "/oauth/token_key";
    String USER_INFO_ENDPOINT = "/oauth/user";
    String USER_APPROVAL_ENDPOINT = "/oauth/confirm_access";
    String ERROR_ENDPOINT = "/oauth/error";
}

 

Transformation PreAuthenticationFilter

Modify PreAuthenticationFilter, parses the new token, request matches, token and user information and information transmission functions.

package com.luas.xmall.gateway.filter;
import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
public class PreAuthenticationFilter extends ZuulFilter {
    private static final int DEFAULT_FILTER_ORDER = 0;
    private Logger logger = LoggerFactory.getLogger(getClass());
    private Set<AntPathRequestMatcher> ignoreAntPathRequestMatchers = new HashSet<>();
    private int order = DEFAULT_FILTER_ORDER;
    private ResourceServerTokenServices tokenServices;
    private TokenExtractor tokenExtractor = new BearerTokenExtractor();
    @Override
    public String filterType() {
        return PRE_TYPE;
    }
    @Override
    public int filterOrder() {
        return order;
    }
    @Override
    public boolean shouldFilter() {
        return !isIgnoreRequest();
    }
    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        this.logger.debug("processing uri {}", request.getRequestURI());
        Authentication authentication = this.tokenExtractor.extract(request);
        if (authentication == null || authentication.getPrincipal() == null) {
            //不会继续往下执行 不会调用服务接口了 网关直接响应给客户了
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseBody("Full authentication is required to access this resource");
            requestContext.setResponseStatusCode(401);
            return null;
        }
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("tokenServices class is {}", this.tokenServices.getClass().getName());
            this.logger.debug("authentication class is {}", authentication.getClass().getName());
            this.logger.debug("authentication is {}", JSONObject.toJSONString(authentication));
        }
        String accessToken = (String) authentication.getPrincipal();
        /* 解析token,将user信息放入request,传递给下游微服务 */
        // 解析token
        OAuth2Authentication oAuth2Authentication = this.tokenServices.loadAuthentication(accessToken);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("OAuth2Authentication class is {}", oAuth2Authentication.getClass().getName());
            this.logger.debug("OAuth2Authentication is {}", JSONObject.toJSONString(oAuth2Authentication));
        }
        // 根据token获取user信息
        // 将user信息放入request,传递给下游微服务
        // requestContext.addZuulRequestHeader("", "");
        return null;
    }
    private boolean isIgnoreRequest() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        for (AntPathRequestMatcher matcher : ignoreAntPathRequestMatchers) {
            if (matcher.matches(request)) {
                return true;
            }
        }
        return false;
    }
    private Set<String> obtainAuthorities(OAuth2Authentication authentication) {
        if (CollectionUtils.isEmpty(authentication.getAuthorities())) {
            return null;
        }
        return authentication.getAuthorities().stream().map(authority -> authority.toString()).collect(Collectors.toSet());
    }
    public Set<AntPathRequestMatcher> getIgnoreAntPathRequestMatchers() {
        return ignoreAntPathRequestMatchers;
    }
    public void setIgnoreAntPathRequestMatchers(Set<AntPathRequestMatcher> ignoreAntPathRequestMatchers) {
        this.ignoreAntPathRequestMatchers = ignoreAntPathRequestMatchers;
    }
    public int getOrder() {
        return order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    public ResourceServerTokenServices getTokenServices() {
        return tokenServices;
    }
    public void setTokenServices(ResourceServerTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }
    public TokenExtractor getTokenExtractor() {
        return tokenExtractor;
    }
    public void setTokenExtractor(TokenExtractor tokenExtractor) {
        this.tokenExtractor = tokenExtractor;
    }
}

Other content, such as parse token, token obtain the user name, the user acquires information (basic information, authority information means information) based on user name and the like, can be realized according to the situation according to its own. Then header manner, the transfer of user information to the services downstream micro, micro downstream service reacquisition by request.

 

Creating PreAuthenticationFilterFactoryBean


Create PreAuthenticationFilterFactoryBean, to achieve and create filter performs a logical separation, to reduce the coupling. PreAuthenticationFilter only responsible for the implementation of the relevant business logic, PreAuthenticationFilterFactoryBean is responsible for the creation of PreAuthenticationFilter created.

package com.luas.xmall.gateway.filter;
import com.luas.xmall.gateway.oauth2.endpoint.AuthorizationServerEndpoints;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class PreAuthenticationFilterFactoryBean implements FactoryBean<PreAuthenticationFilter>, InitializingBean {
    private static final int DEFAULT_FILTER_ORDER = 0;
    private String zuulPrefix;
    private Set<String> ignoreAntPatterns = new HashSet<>();
    private transient Set<AntPathRequestMatcher> ignoreAntPathRequestMatchers = new HashSet<>();
    private int order = DEFAULT_FILTER_ORDER;
    private ResourceServerTokenServices tokenServices;
    private TokenExtractor tokenExtractor = new BearerTokenExtractor();
    @Nullable
    private PreAuthenticationFilter preAuthenticationFilter;
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.tokenServices, "ResourceServerTokenServices bean must not be null.");
        Assert.notNull(this.tokenExtractor, "TokenExtractor bean must not be null.");
        buildIgnorePatterns();
        build();
    }
    public void build() {
        PreAuthenticationFilter preAuthenticationFilter = new PreAuthenticationFilter();
        preAuthenticationFilter.setOrder(this.order);
        preAuthenticationFilter.setTokenServices(this.tokenServices);
        preAuthenticationFilter.setTokenExtractor(this.tokenExtractor);
        preAuthenticationFilter.setIgnoreAntPathRequestMatchers(this.ignoreAntPathRequestMatchers);
        this.preAuthenticationFilter = preAuthenticationFilter;
    }
    private void buildIgnorePatterns() {
        for (String pattern : this.ignoreAntPatterns) {
            this.ignoreAntPathRequestMatchers.add(new AntPathRequestMatcher(pattern));
        }
        // 添加系统默认不拦截路径
        String prefixAntPattern = StringUtils.isBlank(this.zuulPrefix) ? "/*" : this.zuulPrefix + "/*";
        this.ignoreAntPathRequestMatchers.add(new AntPathRequestMatcher(prefixAntPattern + AuthorizationServerEndpoints.TOKEN_ENDPOINT));
        this.ignoreAntPathRequestMatchers.add(new AntPathRequestMatcher(prefixAntPattern + AuthorizationServerEndpoints.CHECK_TOKEN_ENDPOINT));
        this.ignoreAntPathRequestMatchers.add(new AntPathRequestMatcher(prefixAntPattern + AuthorizationServerEndpoints.TOKEN_KEY_ENDPOINT));
    }
    @Override
    public PreAuthenticationFilter getObject() throws Exception {
        return this.preAuthenticationFilter;
    }
    @Override
    public Class<?> getObjectType() {
        return PreAuthenticationFilter.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
    public void ignoreAntPattern(String[] patterns) {
        Assert.notEmpty(patterns, "patterns must be not null.");
        this.ignoreAntPatterns.addAll(Arrays.asList(patterns));
    }
    public void ignoreAntPattern(Set<String> patterns) {
        Assert.notEmpty(patterns, "patterns must be not null.");
        this.ignoreAntPatterns.addAll(patterns);
    }
    public void ignoreAntPattern(String pattern) {
        Assert.hasLength(pattern, "pattern must be not null.");
        this.ignoreAntPatterns.add(pattern);
    }
    public String getZuulPrefix() {
        return zuulPrefix;
    }
    public void setZuulPrefix(String zuulPrefix) {
        this.zuulPrefix = zuulPrefix;
    }
    public Set<String> getIgnoreAntPatterns() {
        return ignoreAntPatterns;
    }
    public void setIgnoreAntPatterns(Set<String> ignoreAntPatterns) {
        this.ignoreAntPatterns = ignoreAntPatterns;
    }
    public Set<AntPathRequestMatcher> getIgnoreAntPathRequestMatchers() {
        return ignoreAntPathRequestMatchers;
    }
    public void setIgnoreAntPathRequestMatchers(Set<AntPathRequestMatcher> ignoreAntPathRequestMatchers) {
        this.ignoreAntPathRequestMatchers = ignoreAntPathRequestMatchers;
    }
    public int getOrder() {
        return order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    public ResourceServerTokenServices getTokenServices() {
        return tokenServices;
    }
    public void setTokenServices(ResourceServerTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }
    public TokenExtractor getTokenExtractor() {
        return tokenExtractor;
    }
    public void setTokenExtractor(TokenExtractor tokenExtractor) {
        this.tokenExtractor = tokenExtractor;
    }
}


 

System Configuration Manager

Creating SysProperties class management system-related configuration items. Wherein, ignoreAntPatterns style is the ant path matching rules, such as / a / **, / b / * , etc. , then the configuration, all of which match the rules of the request, not for authentication .

package com.luas.xmall.gateway.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Set;
@ConfigurationProperties(prefix = "sys.zuul")
public class SysProperties {
    private PreAuthenticationFilter preAuthenticationFilter = new PreAuthenticationFilter();
    public PreAuthenticationFilter getPreAuthenticationFilter() {
        return preAuthenticationFilter;
    }
    public void setPreAuthenticationFilter(PreAuthenticationFilter preAuthenticationFilter) {
        this.preAuthenticationFilter = preAuthenticationFilter;
    }
    public static class PreAuthenticationFilter {
        private boolean enabled = true;
        private int order = 0;
        private Set<String> ignoreAntPatterns;
        public boolean isEnabled() {
            return enabled;
        }
        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
        public int getOrder() {
            return order;
        }
        public void setOrder(int order) {
            this.order = order;
        }
        public Set<String> getIgnoreAntPatterns() {
            return ignoreAntPatterns;
        }
        public void setIgnoreAntPatterns(Set<String> ignoreAntPatterns) {
            this.ignoreAntPatterns = ignoreAntPatterns;
        }
    }
}

After performing mvn clean compile, in yml configuration, the system will prompt the configuration items, as shown in FIG.

Note To automatically prompt the need to add spring-boot-configuration-processor dependent.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

 

System Configuration

Creating SysConfiguration, system-level configuration, such as PreAuthenticationFilter creation and so on.

package com.luas.xmall.gateway.configuration;
import com.luas.xmall.gateway.filter.PreAuthenticationFilter;
import com.luas.xmall.gateway.filter.PreAuthenticationFilterFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.util.CollectionUtils;
@Configuration
@EnableConfigurationProperties(SysProperties.class)
public class SysConfiguration {
    private SysProperties sysProperties;
    private ZuulProperties zuulProperties;
    public SysConfiguration(SysProperties sysProperties, ZuulProperties zuulProperties) {
        this.sysProperties = sysProperties;
        this.zuulProperties = zuulProperties;
    }
    @Bean
    @ConditionalOnProperty(prefix = "sys.zuul", name = "pre-authentication-filter.enabled", havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean(PreAuthenticationFilter.class)
    public PreAuthenticationFilterFactoryBean preAuthenticationFilter(@Qualifier("userInfoTokenServices") ResourceServerTokenServices tokenServices) {
        SysProperties.PreAuthenticationFilter preAuthFilter = this.sysProperties.getPreAuthenticationFilter();
        PreAuthenticationFilterFactoryBean preAuthenticationFilterFactoryBean = new PreAuthenticationFilterFactoryBean();
        preAuthenticationFilterFactoryBean.setZuulPrefix(this.zuulProperties.getPrefix());
        preAuthenticationFilterFactoryBean.setTokenServices(tokenServices);
        preAuthenticationFilterFactoryBean.setOrder(preAuthFilter.getOrder());
        if (!CollectionUtils.isEmpty(preAuthFilter.getIgnoreAntPatterns())) {
            preAuthenticationFilterFactoryBean.ignoreAntPattern(preAuthFilter.getIgnoreAntPatterns());
        }
        return preAuthenticationFilterFactoryBean;
    }
}

Note that, as configured sys.zuul.pre-authentication-filter.enabled = false, then PreAuthenticationFilter will not be created , that is no longer the pre-authentication.

 

Server Configuration transformation of resource category

Modify the resource server configuration class ResourceServerConfiguration, the introduction of endpoint management interface for system configuration, implementation-dependent endpoint does not intercept.

package com.luas.xmall.gateway.configuration;
import com.luas.xmall.gateway.oauth2.endpoint.AuthorizationServerEndpoints;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    private String prefixAntPattern;
    public ResourceServerConfiguration(ZuulProperties zuulProperties) {
        this.prefixAntPattern = StringUtils.isBlank(zuulProperties.getPrefix()) ? "/*" : zuulProperties.getPrefix() + "/*";
    }
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers(this.prefixAntPattern + AuthorizationServerEndpoints.TOKEN_ENDPOINT).permitAll()
                .antMatchers(this.prefixAntPattern + AuthorizationServerEndpoints.TOKEN_KEY_ENDPOINT).permitAll()
                .antMatchers(this.prefixAntPattern + AuthorizationServerEndpoints.CHECK_TOKEN_ENDPOINT).permitAll()
                .anyRequest().authenticated()
        ;
    }
}

There are two points here, the more important.

First, as with PreAuthenticationFilterFactoryBean class, why add / * or / zuulPrefix / *? Pre-authentication type filter is a pre filter, this time for the whole path REQUEST request, comprising zuul whole prefix, prefix each routing rules, it is necessary to precisely match.

The second point, why not / **? / * And / ** are ant type matching rule category, meaning different. / ** optionally matched, i.e., any level. The / * only match level. The / ** / ** / oauth / token, matches / gateway / auth / a / oauth / token, can match / a / b / c / gateway / auth / a / b / c / oauth / token. The / * / * / oauth / token, can only match / gateway / auth / oauth / token. All rigorous reasons, configured / * is optimal.

 

verification

In turn starts xmall-product, xmall-auth, xmall-zuul, ports are 8080, 7777,5566.

Through the gateway calling authorized service authorization, the address is http: // localhost:? 5566 / gateway / auth / oauth / token client_id = client_1 & client_secret = 123456 & username = user_1 & password = 123456 & scope = server & grant_type = password.

After the request, return authorization result.

header authorized to carry way, visit http: // localhost: 8080 / sku / 1122, normal access.

Reconstruction project completed, you can find the code than the original, capable of some responsibilities clearer, more logical and clear. At the same time, easier to future extensions. Follow-up, another serving gateway related articles, all this reconstructed version as the basis for further transformation.

 

Source


github

https://github.com/liuminglei/SpringCloudLearning/tree/master/14/

gitee

https://gitee.com/xbd521/SpringCloudLearning/tree/master/14/

 

 

This article is [Galaxy] original architect, in an article for reprint please indicate the author and source of the obvious.

Micro-channel search [architect] Galaxy, find more exciting content.

 

发布了29 篇原创文章 · 获赞 1 · 访问量 2224

Guess you like

Origin blog.csdn.net/liuminglei1987/article/details/104199450