springcloud+zuul+oauth2 实现服务统一认证(新增自定义异常返回,获取认证服务器token自定义异常后面在搞) - Go语言中文社区

springcloud+zuul+oauth2 实现服务统一认证(新增自定义异常返回,获取认证服务器token自定义异常后面在搞)


第一种配置方式:zuul只负责转发,流控等(不负责认证)

1.介绍

(1) eureka服务发现,各服务配置就不发了,只看关于认证这块;

(2)

服务名 端口号 备注
auth 8082 认证服务器
mechant 8081 资源服务器
zuul 80 网关

2 配置认证服务器

(1)添加依赖(oauth已经包含了security)

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

(2)配置认证

@Configuration
public class MyConfig {

    //设置用户信息处理类,这里为了测试使用密码123,用户名随意
    @Component
    public static class MyUserDetailsService implements UserDetailsService {
        @Autowired
        private PasswordEncoder passwordEncoder;

        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return new User(username, passwordEncoder.encode("123"),
                    AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
        }
    }

    //认证服务器
    @EnableAuthorizationServer
    @Configuration
    public static class Authorization extends AuthorizationServerConfigurerAdapter {

        @Autowired
        AuthenticationManager authenticationManager;
        @Autowired
        BCryptPasswordEncoder bCryptPasswordEncoder;
        @Autowired
        MyUserDetailsService myUserDetailsService;

        //为了测试客户端与凭证存储在内存(生产应该用数据库来存储,oauth有标准数据库模板)
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("client") // client_id
                    .secret(bCryptPasswordEncoder.encode("123")) // client_secret
                    .authorizedGrantTypes("authorization_code", "password") // 该client允许的授权类型
                    .scopes("app"); // 允许的授权范围
        }

        //authenticationManager配合password模式使用,tokenstore生产可用redis
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)
                    .tokenStore(new InMemoryTokenStore())
                    .userDetailsService(myUserDetailsService);
        }

        //配置token状态查询
          @Override
         public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)
                    .tokenStore(new InMemoryTokenStore())
                    .userDetailsService(myUserDetailsService);
            endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET);
            DefaultTokenServices tokenService = new DefaultTokenServices();
            tokenService.setTokenStore(endpoints.getTokenStore());
            tokenService.setSupportRefreshToken(true);
            tokenService.setClientDetailsService(endpoints.getClientDetailsService());
            tokenService.setTokenEnhancer(endpoints.getTokenEnhancer());
            tokenService.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); //30天
            tokenService.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(50)); //50天
            tokenService.setReuseRefreshToken(false);
            endpoints.tokenServices(tokenService);
        }

    //认证服务器需配合Security使用
    @Configuration
    public static class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

        //这里只验证是否带有token的失败返回authenticationEntryPoint
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .httpBasic().and()
                    .csrf().disable()
                    .exceptionHandling()
                    .authenticationEntryPoint((req, resp, exception) -> {
                        resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                        resp.getWriter().write(new ObjectMapper().writeValueAsString(new HashMap() {{
                            put("status", 0);
                            put("error", "没有权限");
                        }}));
                    }).and()
                    .authorizeRequests().anyRequest().authenticated();
        }
    }
}

(3)提供客户信息

@RestController
public class ResourceWeb {

    @GetMapping("/member")
    public Principal user(Principal member) {
        //获取当前用户信息
        return member;
    }
}

(4)流程

这里用的是密码方式,一般用于本地服务自身资源的调用;

(client,secret)代表客户端账号密码,在该测试中,这个客户端其实就表示本地的服务.如果是第三方,则申请到账号密码后,可以在得到用户授权后进行资源调用;

(username,password)就是本服务的用户主体了,所有客户端获取资源都需要本地用户的登陆成功授权后方可获取,所以用到了security的登陆策略;

 

3.配置资源服务器(本地资源)

(1)同样添加oauth依赖

(2)配置远程认证服务:

security:
  oauth2:
    resource:
      user-info-uri: http://localhost:8082/member
      prefer-token-info: false

(3)增加自定义异常返回,以及能够验证oauth2的参数

@Configuration
public class MyConfig {

    //配置资源服务器
    @Configuration
    @EnableResourceServer
    public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().exceptionHandling()
                    .authenticationEntryPoint(new EntryPint())
                    .and().authorizeRequests()
                    .antMatchers("/manage/shutdown", "/open/**")
                    .access("#oauth2.hasScope('app')").anyRequest().permitAll();
        }
        
          @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.accessDeniedHandler((request,response,e)->{
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.failure("授权失败")));
            });
            resources.authenticationEntryPoint((request,response,e)->{
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.failure("token失败")));
            });
        }
}

4.配置zuul

zuul:
  #routes:
  #  MECHANT:
  #    service-id: MECHANT
  #    path: /mechant/**
  strip-prefix: true    #当为false时,请求地址->MECHANT->http://localhost:8081/api/mechant/ping,返回404
  prefix: /api          #请求前缀
  sensitive-headers:   #此处不写则无法携带header;如果客户端在发请求是带了X-ABC,那么X-ABC不会传递给下游服务
  #ignoredHeaders: X-ABC    #如果客户端在发请求是带了X-ABC,那么X-ABC依然会传递给下游服务。但是如果下游服务再转发就会被过滤

 

5.测试

(1)申请token(使用zuul访问)

或者直接使用纯表单方式提交,不带header:

(2)如果是客户端模式(client_credentials),申请token如下:

 

(3)使用token(使用zuul访问)

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/myth_g/article/details/89811409
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-27 17:12:00
  • 阅读 ( 4510 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢