学习笔记随记-2019-01-11-Spring Cloud Security+Oauth2+JWT权限认证与zuul结合遇到的问题

版权声明:本文章属原创,转载请注明出处,谢谢。 https://blog.csdn.net/nvluco/article/details/86406324

20190111

1.学到的知识

1.1.记录问题

1.1.1回到昨天的问题,继续解决:
  • 报错二:
    postman的返回结果信息如下:
    token转换错误

将日志输出转换为debug,查看控制台日志如下:

2019-01-11 00:13:04.663 DEBUG 21056 --- [http-nio-9090-exec-6] o.s.boot.actuate.audit.listener.AuditListener                          : AuditEvent [timestamp=2019-01-10T16:13:04.663Z, principal=access-token, type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.BadCredentialsException, message=Cannot convert access token to JSON}]
2019-01-11 00:13:04.666 DEBUG 21056 --- [http-nio-9090-exec-6] o.s.security.web.header.writers.HstsHeaderWriter                       : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@64132b53
2019-01-11 00:13:04.668 DEBUG 21056 --- [http-nio-9090-exec-6] o.s.s.o.p.error.DefaultOAuth2ExceptionRenderer                         : Written [error="invalid_token", error_description="Cannot convert access token to JSON"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@eb68d2f]
2019-01-11 00:13:04.669 DEBUG 21056 --- [http-nio-9090-exec-6] o.s.s.web.context.SecurityContextPersistenceFilter                     : SecurityContextHolder now cleared, as request processing completed

针对报错信息,自己先回头看了下自己写的逻辑代码,从源头出发,发现是这个 authorization 的前缀出现了问题,也就是请求头应该是 Basic,而不是Bearer,前者是用来 做服务端认证的标示,后者是权限信息判断的标示。因为标示不对,所以一直提示转换问题。

对请求头的参数进行了打印:

{request=org.springframework.cloud.netflix.zuul.filters.pre.Servlet30RequestWrapper@77410b21, response=com.netflix.zuul.http.HttpServletResponseWrapper@50e9453a, zuulRequestHeaders={authorization=Bearer dXNlci1zZXJ2aWNlOjEyMzQ1Ng==, content-type=application/x-www-form-urlencoded;charset=UTF-8}, zuulEngineRan=true, executedFilters=ServletDetectionFilter[SUCCESS][0ms], Servlet30WrapperFilter[SUCCESS][0ms], FormBodyWrapperFilter[SUCCESS][7ms], isDispatcherServletRequest=true}

对于登录接口过滤的时候,原写法如下(错误)

	ctx.addZuulRequestHeader("Authorization", "Bearer " + accessToken);

正确的写法应该是:

	ctx.addZuulRequestHeader("Authorization",  accessToken);
1.1.2新问题:无权访问:

错误日志如下:
一直提示无权访问

2019-01-11 20:27:10.170 DEBUG 16776 --- [http-nio-9090-exec-2] o.s.s.w.access.intercept.FilterSecurityInterceptor                     : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@f70a0382: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2019-01-11 20:27:10.170 DEBUG 16776 --- [http-nio-9090-exec-2] o.s.security.access.vote.AffirmativeBased                              : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@305050b0, returned: -1
2019-01-11 20:27:10.170 DEBUG 16776 --- [http-nio-9090-exec-2] o.s.boot.actuate.audit.listener.AuditListener                          : AuditEvent [timestamp=2019-01-11T12:27:10.170Z, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={details=org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null, type=org.springframework.security.access.AccessDeniedException, message=Access is denied}]
2019-01-11 20:27:10.171 DEBUG 16776 --- [http-nio-9090-exec-2] o.s.security.web.access.ExceptionTranslationFilter                     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)

  • 关键信息:
    Access is denied (user is anonymous); redirecting to authentication entry point
解决办法一:

在网上找到一些相关的资料,说是过滤器没有被调用。
帖子:
https://stackoverflow.com/questions/44171633/spring-boot-oauth2-access-is-denied-user-is-anonymous-redirecting-to-authen?rq=1

解决办法一
然后自己再控制台找,发现是被调用了的:
已经被调用了
过滤器已经被调用了

  • 结果:排除这个问题。
解决办法二:

解决办法二
作者的遇到的问题应该是 resource-service 远程调用uaa-service的时候需要权限信息,这个是没错的,这里他就需要上面那个 basic 开头的参数,来对请求者进行判断,看是否符合条件。看到这一点,自己也加了 .antMatchers("/oauth/token").permitAll() ,加完之后,发现错误还是之前那个,那么这个时候,就排查了不是这个uaa的问题了。
在后面的时候,想了下,看日志可以知道还没执行到这一步呢。这一步当时有点多余(脑子有点糊涂了)。

  • 结果:无效,不是这个问题。
解决办法三:

找到的资料:
https://blog.coding.net/blog/Explore-the-cache-request-of-Security-Spring

说到得是 Spring Security 缓存请求 这个点了,大意就是第一次请求的时候,认证的信息存放在header中,第一次请求受保护资源时,请求头中不包含认证信息 ,验证失败,该请求会被缓存,之后即使用户填写了信息,也会因为request被恢复导致信息丢失从而认证失败,所以解决办法就是不缓存request。

默认情况下,三种request不会被缓存。

  • 请求地址以/favicon.ico结尾
  • header中的content-type值为application/json
  • header中的X-Requested-With值为XMLHttpRequest

好,针对这一点,自己也将不缓存request,在HttpSecurity中设置下面:

http.requestCache().requestCache(new NullRequestCache());
  • 结果:不理想,仍不能解决问题。

后面思考总结的时候,想了下,如果是缓存的问题,那么我关闭服务的时候,就已经清掉了缓存呀,这也是一个验证的方式,但是搞了一天,当时思路就很迷了。

解决办法四:

自己问了之前用过Zuul和springcloud的朋友,讨论了很久之后,将问题定位到 zuul路由这个点上了。朋友提点可能是我的url设置有问题。
也就是说,zuul路由的时候,设置了会为每一个 微服务附带一个url前缀,根据我的设置:请看代码一,结合着下面的设置,可以知道我是给我 sources-service微服务,增设了 /a/ 这个前缀。如此,自己将自己的接口重新更改过来,在原先的接口 /a/login 更改为 /login 。由此解决了这个搞了一天的问题。

  • 分析
    按照上面所说,zuul路由之前会为每一个微服务增加一个你自设得 url前缀,而此时如果你接口使用的是 /a/login,那么在路由之前的地址就是 /a/a/login,这样的因为不符合 filter条件也将被过滤掉,提示 接口不存在。所以就是在接口处,去掉 /a/ ,同时 HttpSecurity 中,增设开放不需要authorization的 url地址。也就是再下面代码二:

  • 代码一

zuul:
  routes:
    api-a:
      path: /a/**
      serviceId: sources-service
    api-b:
      path: /auth/**
      serviceId: uaa-service
  • 代码二
@Override
  public void configure(HttpSecurity http) throws Exception {
  http
    // 由于使用的是JWT,我们这里不需要csrf
    .csrf().disable()
    // 基于token,所以不需要session
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
    .authorizeRequests()
      // 允许对于网站静态资源的无授权访问
      .antMatchers(
          HttpMethod.GET,
          "/",
          "/*.html",
          "/favicon.ico",
          "/**/*.html",
          "/**/*.css",
          "/**/*.js"
      ).permitAll()
      // 对于获取token的rest api要允许匿名访问
      .antMatchers("/auth/**").permitAll()
      .antMatchers("/swagger-ui.html","/a/login","/a/register","/login").permitAll()
      // 除上面外的所有请求全部需要鉴权认证
      .anyRequest().authenticated();
  // 禁用缓存
  http.headers().cacheControl();

  }

猜你喜欢

转载自blog.csdn.net/nvluco/article/details/86406324
今日推荐