Java 杂谈程序员java

第十四节 springcould zuul边缘路由的使用

2018-10-14  本文已影响4人  勃列日涅夫

本微服务中使用zuul 服务网关作为边缘路由,在oauth2中同时它本身也是资源服务

  1. 生成证书keystore.jks,生成方式可参考:
    (安全服务模块中的证书生成部分)[https://www.jianshu.com/p/6e11e6ab0a85]
  2. application.yml配置
#ssl配置
server:
    ssl:
        key-store: classpath:keystore.jks
        key-store-password: password
        key-password: password
    port: 8765
    compression:
        enabled: true
#oauth2 配置
security:
  oauth2:
    clientId: client
    clientSecret: password
    userAuthorizationUri: https://localhost:9001/oauth/authorize
    grant-type: password
    scope: apiAccess
    access-token-uri: https://localhost:9001/oauth/token
    resource:
      userInfoUri: https://localhost:9001/user
    authorization:
      checkTokenAccess: http://localhost:9001/oauth/check_token
  basic:
    enabled: false
# zuul路由配置
zuul:
    ignoredServices: "*"
    routes:
        restaurantapi:
            path: /api/**
            serviceId: api-service
            stripPrefix: true
        resource:
          path: /api/**
          url: http://localhost:9000
        user:
            path: /user/**
            url: https://localhost:9001/user
### 其他配置略。。。
  1. Security和配置
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private BaseUserDetailService baseUserDetailService;
    //Spring Security 4.x -> 5.x  會無法直接注入AuthenticationManager,下面解決
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    /**
     * 用户验证
     * @param auth
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(daoAuthenticationProvider());
        auth.userDetailsService(baseUserDetailService) .passwordEncoder(passwordEncoder());
    }
    /**
     * @param http
     * WebSecurityConfigurerAdapter和ResourceServerConfigurerAdapter二者是分工协作的
     * @throws Exception
     * WebSecurityConfigurerAdapter不拦截oauth要开放的资源
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http    // 配置登陆页/login并允许访问
                .formLogin().permitAll()
                // 登出页
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
                // 其余所有请求全部需要鉴权认证
                .and().authorizeRequests().anyRequest().authenticated()
                // 由于使用的是JWT,我们这里不需要csrf
                .and().csrf().disable();
    }
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置userDetailsService
        provider.setUserDetailsService(baseUserDetailService);
        // 禁止隐藏用户未找到异常
        provider.setHideUserNotFoundExceptions(false);
        // 使用BCrypt(BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥)进行密码的hash
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }
    @Bean
    public PasswordEncoder passwordEncoder(){
        return  new BCryptPasswordEncoder();
    }
}
  1. Resource配置
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    //spring.security.oauth2.client

//    @Autowired
//    private OAuth2ClientProperties oAuth2ClientProperties;
//    @Autowired
//    private AuthorizationServerProperties authorizationServerProperties;
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.tokenServices(tokenServices());//.resourceId(SPARKLR_RESOURCE_ID);
    }

  @Bean
  public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
//这里配置远程校验token的地址,(也可以使用其他方式,例如本地校验)
        remoteTokenServices.setCheckTokenEndpointUrl("https://security-service/oauth/check_token");
//为方便测试使用硬编码,要和security-server配置的相同(security服务会校验客户端信息)
        remoteTokenServices.setClientId("client");
        remoteTokenServices.setClientSecret("password");
      remoteTokenServices.setRestTemplate(restTemplate());
//使用默认令牌数据的存储
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
        return remoteTokenServices;
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        //httpRequestFactory()
        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter;
                jsonConverter.setObjectMapper(new ObjectMapper());
                jsonConverter.setSupportedMediaTypes(ImmutableList.of(new MediaType("application", "json", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET), new MediaType("text", "javascript", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)));
            }
        }
        return restTemplate;
    }
        @Bean
        public AccessTokenConverter accessTokenConverter() {
        return new DefaultAccessTokenConverter();
    }
}
@Component
public class MyFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(MyFilter.class);
    @Override
    public String filterType() {
        // 前置过滤器
        return "pre";
    }
    @Override
    public int filterOrder() {
        // 优先级为0,数字越大,优先级越低
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        // 是否执行该过滤器,此处为true,说明需要过滤
        return true;
    }

    //执行过程方法
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        Object accessToken = request.getHeader("Authorization");
//      Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            log.warn("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}
            return null;
        }
        log.info("ok");
        return null;
    }
}

其他具体配置及功能可参考源码zuul服务源码
需要说明需要启动本zuul项目,需要依赖eureka server、security-server、以及其他业务服务

上一篇 下一篇

猜你喜欢

热点阅读