Spring Oauth授权码方式与zuul整合

Spring Oauth授权码方式与zuul整合

目前流行分布式架构,通常会把鉴权做成一个独立的服务,给其他服务使用,有时候甚至给第三方使用。此时采用oauth授权再合适不过了。而Spring全家桶自然也考虑到了这一点,提供了oauth包。本身用spring的oauth做成独立的服务没有什么问题,而微服务中,注重高可用,那就意味着我们必定不止部署一个服务,而多个服务之间的负载必然要用到zuul(毕竟这也是spring的亲儿子)。

通常给第三方授权都是用授权码的方式进行,比如QQ或者微信。授权码方式意味着用户输入用户名和密码的过程在我们的系统上,这种方式更加安全可靠。

Spring Oauth的认证过程大概是下面这样

  1. 先发送一个/oauth/authorize请求,系统先判断请求中的clientId是否正确,正确的话再判断用户是否已经登录,是则继续,不是则跳转到登录页面。
  2. 跳转到登录页面之后,用户输入用户名和密码,校验正确再重定向到/oauth/authorize。
  3. /oauth/authorize判断用户已经登录跳转(转发)到授权页面,让用户选择授权或者拒绝,还可以勾选哪些资料授权。
  4. 最后跳转到一开始请求中包含的授权成功跳转的页面,spring oauth可以直接配置,不需要在请求中带。

有没有发现上面的过程中,完成登录页填写后要做页面重定向,而这个过程是在oauth授权服务里面做的,这会导致一个问题,因为请求是经过zuul转发的,而重定向发生在服务中,会导致跳转之后的地址变成了服务地址,而非zuul的地址了。比如下面这个例子。

http://localhost:9080是zuul,http://localhost:9081是auth,通过zuul访问auth,要http://localhost:9080/oa/auth,我们服务固定前缀/oa/auth。

通过zuul授权过程如下:

1)一开始的授权地址:http://localhost:9080/oa/auth/oauth/authorize?scope=all&response_type=code&client_id=oa

2)没登录的话跳转到登录页面,这边我还可以指定下,http://localhost:9080/oa/auth/login

3)登录完成之后重定向到开始的授权页面,就变成了http://localhost:9080/oauth/authorize?scope=all&response_type=code&client_id=oa,前面的oa/auth没了。

要解决这个问题,需要在zuul里面对地址进行处理,因为spring自带的地址都是基于/oauth开头的,可以在filter里面针对/oauth开头的地址也转发给oauth服务,然后再做些简单的处理。

 

以下为例子,我们的oauth服务名为provider-oa-auth

 

Zuul的配置

  routes:

    oa-oauth-route:

      path: /oauth/**

      sensitiveHeaders:

      service-id: provider-oa-auth

      ribbon:

        eager-load:

          enabled: true

        MaxAutoRetries: 1

        MaxAutoRetriesNextServer: 2

        OkToRetryOnAllOperations: false

        ConnectTimeout: 10000

        ReadTimeout: 10000

    oa-auth-route:

      path: /oa/auth/**

      sensitiveHeaders:

      service-id: provider-oa-auth

      ribbon:

        eager-load:

          enabled: true

        MaxAutoRetries: 1

        MaxAutoRetriesNextServer: 2

        OkToRetryOnAllOperations: false

        ConnectTimeout: 10000

        ReadTimeout: 10000

 

Zuul的filter配置

@Component

public class OauthFilter extends AbstractFilter {

 

@Override

public boolean shouldFilter() {

return true;

}

 

@Override

public Object run() throws ZuulException {

RequestContext ctx = RequestContext.getCurrentContext();

HttpServletRequest request = ctx.getRequest();

String url = request.getRequestURI();

 // 此处针对不包含/oa/auth前缀,但包含了/oauth的路径

if(StringUtils.isNotBlank(url) && url.contains("/oauth") && !url.contains("/oa/auth")) {

url = url.substring(url.lastIndexOf("/oauth"));

ctx.put(FilterConstants.REQUEST_URI_KEY, url);

}

return null;

}

 

@Override

public String filterType() {

return FilterConstants.PRE_TYPE;

}

 

@Override

public int filterOrder() {

return 6;

}

 

}

 

provider-oa-authoauth配置

@Configuration

public class OaAuthorizationConfig {

@Configuration

@EnableAuthorizationServer

public class OaAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

@Resource

private DataSource dataSource;

 

@Autowired

private AuthenticationManager authenticationManager;

 

@Autowired

private RedisConnectionFactory connectionFactory;

 

@Autowired

private OaUserDetailsService userDetailsService;

 

@Autowired

private PasswordEncoder passwordEncoder;

 

@Bean

public RedisTokenStore tokenStore() {

RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);

return redisTokenStore;

}

 

/**

 * 配置授权服务器的安全,意味着实际上是/oauth/token端点。 /oauth/authorize端点也应该是安全的

 * 默认的设置覆盖到了绝大多数需求,所以一般情况下你不需要做任何事情。

 */

@Override

public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")

.allowFormAuthenticationForClients();

}

 

/**

 * 配置ClientDetailsService

 * 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。

 * 至少配置一个client,否则服务器将不会启动。

 */

@Override

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

clients.jdbc(dataSource).passwordEncoder(passwordEncoder);

}

 

/**

 * 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的

 * 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager

 */

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService).tokenStore(tokenStore())

.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);

}

}

 

@Configuration

@EnableResourceServer

public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

 

@Override

public void configure(HttpSecurity http) throws Exception {

http.requestMatchers().antMatchers("/api/**")

        .and()

        .authorizeRequests()

        .antMatchers("/api/**").authenticated();

}

}

 

provider-oa-authsecurity配置

@Configuration

@EnableWebSecurity

@Order(10)

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

 

@Autowired

private OaUserDetailsService userDetailsService;

 

@Autowired

private PasswordEncoder passwordEncoder;

    

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth

            .userDetailsService(userDetailsService)

            .passwordEncoder(passwordEncoder);

    }

 

    @Override

    @Bean

    public AuthenticationManager authenticationManagerBean() throws Exception {

        return super.authenticationManagerBean();

    }

 

@Override

    protected void configure(HttpSecurity http) throws Exception {

http.csrf().disable()

        .authorizeRequests()

        .antMatchers("/", "/user/**", "/oauth/**", "/actuator/**", "/v2/api-docs").permitAll()

        .anyRequest().authenticated()

        .and()

        .formLogin()

        .loginPage("/oauth/login")

        .loginProcessingUrl("/oauth/form")

        .failureUrl("/oauth/login?error")

        .permitAll()

        .and()

        .sessionManagement()

        .invalidSessionUrl("/oauth/login")

        .maximumSessions(1)

        .expiredSessionStrategy(new SessionInformationExpiredStrategyImpl());

    }

 

/*忽略静态资源*/

    @Override

    public void configure(WebSecurity web) {

        web.ignoring().antMatchers("/resources/static/**");

    }

}

 

zuul和oauth源代码下载

链接: https://pan.baidu.com/s/15narhBEG3Uk77kyTRf_vJw 提取码: np6m

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值