OAuth2保护微服务 案例实战

OAuth2保护微服务 案例实战

前言

不得不吐槽一下,一些经典书籍的案例 真的已经过时了,首先依赖包不适用,案例不正确,代码欠缺,而且很多关键位置的代码并没有贴出来,这对于许多不仅仅是看书,也想动手敲一敲案例的学习者来说很不友好!!一度都想跳过这一章,记录使用使用OAuth2+SpringSecurity+SpringCloud时遇到的一系列坑。

参照《Spring微服务实战》

1. 依赖包问题

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

注意起步依赖 spring-cloud-starter···,原书中的spring-cloud-···真的让我吐了,一直怀疑可能是因为我版本的问题,查了很久之后发现,确实是包的问题。

工程结构如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O9df0GMr-1591115573053)(C:\Users\a\AppData\Roaming\Typora\typora-user-images\1591104088075.png)]

2.服务引导类Application

@SpringBootApplication
@RestController
@EnableResourceServer
@EnableAuthorizationServer //OAuth2服务
public class OAuth2Application {
    @GetMapping(value = "/auth/user",produces = "application/json")
    public Map<String,Object> user(OAuth2Authentication user){
        Map<String,Object> userInfo=new HashMap<>();
        userInfo.put("user",user.getUserAuthentication().getPrincipal());
        userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
        return userInfo;
    }


    public static void main(String[] args){
        SpringApplication.run(OAuth2Application.class,args);
    }
}

OAuth2服务的引导类,除了通过注解声明该服务是OAuth2服务外,添加了一个 /user 路径映射,当有客户端服务访问由OAuth2保护的服务时会调用这个端点。(说直白点就是,客户端服务拿着授权令牌Authorization时,对这个Authorization进行解析,回返回对应的user和authorities)。

此类在原书中仍然是个坑,在指向验证服务时,原书里的userInfoUri:/auth/user,而原书里的启动类却是RequestMapping("/user"),说是会映射到/auth/user路径上去,然而我在试验时,并不是这样,而是要通过/user才可以,这不是给自己添麻烦吗?! (我猜测本书中可能有某个地方设置一个类似路径prefix这样的东西,因为其实所有的请求路径都是 ip+port/auth/···· 然而 它实际的编码里 并没有添加/auth )

3.配置类OAuth2

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() //指定为内存存储 jdbc存储或内存存储
                .withClient("orgFeign") //注册的应用程序名称
                .secret("thisissecret") //密钥
                .authorizedGrantTypes(  //授权类型
                        "password",
                        "client_credentials"
                ).scopes("webclient","mobileclient"); //作用域
        //定义验证服务注册的客户端应用程序
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
//                .tokenKeyAccess("permitAll()")  // auth/oauth/token_key是公开的
//                .checkTokenAccess("permitAll()") // auth/oauth/check_token是公开的
//                .allowFormAuthenticationForClients() //表单验证(申请令牌)
                .passwordEncoder(NoOpPasswordEncoder.getInstance());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }
}

该类定义了哪些应用程序可以访问由OAuth2保护的服务,重点关注覆写的两个configure方法。

第一个configure中,定义了哪些客户端注册到OAuth2中,withClient()和secret()是客户端应用程序获取OAuth2访问令牌的名称和密钥,authorizedGrantTypes()为授权类型列表 这里指定为密码类型,scopes为作用域,为自定义的内容 可以用来做细粒度的授权规则。

第二个configure中,有三行注释掉的代码,其实这是我出现401时参照别人的解决方案,但实际无济于事,我401的时候加了还是401,我解决正常运行后注释掉也不影响它照常工作。

4. 配置类Security

与上一个配置类不同,上一个是配置OAuth2相关的,这个配置类是配置SpringSecurity的用户id 密码和角色。与单独使用SpringSecurity进行权限控制资源保护的基本一样。

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
                .withUser("jam").password("123456").roles("USER")
                .and()
                .withUser("yang").password("123456").roles("USER","ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/oauth/token").permitAll();
    }
}

同样重点也是两个configure方法,本文的例子中为了方便选择了一个弃用的密码编码器NoOpPasswordEncoder,官方已不推荐使用,第一个configure方法采用基于内存的方式注册了两个用户jam和yang;第二个configure方法才是解决了我之前出现401的关键步骤之一!!!原书中并没有提到需放通 /oauth/token 这一路径,实属有点坑人。

以上就可以尝试起通过OAuth2服务来获取令牌以及验证授权。

5. Postman测试

在这里插入图片描述

首先 是这里的路径问题 原书中是 ip:port/auth/oauth/token 同理,用加上auth之后仍然是不行的,加上前面Security配置类上我们放通的是 /oauth/token(只需要放通,背后映射做的处理由OAtuh2帮我们完成),所以正确的路径如图,需要选择Authorization的Type为Basic Auth,右边有两个参数要填写,这里的Username和Password分别对应OAuth2配置类的withClient()和secret()

在这里插入图片描述

除了Authorization之外,还需要写入对应的参数,这里需要在body里选择表单数据 form-data,然后分别写grant_type授权类型,scope作用域,username和password(注册的用户的用户名密码),前两个参数参见 3.OAtuh2配置类,用户名密码是Security配置类上注册的。这里有两个 注释掉的参数 client_id 和client_secret 这是一些博客上解决访问/auth/token报401的方案,但是我这里行不通,所以我把它注掉了。

结果可以看到 access_token和token_type 此为OAuth2颁发的令牌,而我们可以拿着这个令牌去OAuth2服务器验证权限,还记得2.的引导类中的那个地址映射吗?可以在HTTP头部携带这个令牌去验证。

在这里插入图片描述

至此,授权令牌的颁发与验证就已经完成了。

6. 配置被保护的服务

// 本文以一个 OrgnizationApplication-9001为例

6.1 yaml与引导类

server:
  port: 9001

security:
  oauth2:
    resource:
      userInfoUri: http://localhost:4396/auth/user
@SpringBootApplication
@EnableResourceServer  //注解为受OAuth2保护的资源
@EnableEurekaClient
public class OrganizationApplication9001 {
    public static void main(String[] args){
        SpringApplication.run(OrganizationApplication9001.class,args);
    }
}

@EnableResourceServer 会强制执行一个Filter,会对所有到达改服务的请求执行Filter检查传入的HTTP首部是否存在OAuth2访问令牌,然后通过回调yaml中的uri来查看令牌是否有效。

6.2 测试的Controller

@RestController
public class OrganizationController {
    @GetMapping("/organization")
    public String getMsg(){
        return "This is organization  9001";
    }
}

6.2 定义访问规则

@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers(HttpMethod.GET,"/organization")
                .hasRole("ADMIN")
                .anyRequest()
                .authenticated();
    }
}

这里定义了 /organization 资源路径的规则,需要ADMIN角色,(本文中的两个用户jam为user角色,而yang为user和admin角色),所以可以通过分别向OAuth获取这两个用户的access_token 然后访问这个服务 加以验证。

6.3 访问被保护的服务

如图,这个截图例子测试的是jam用户也即user角色的结果,会发现403拒绝访问,达到了预期的效果,yang用户即admin角色可以自行测试,是可以正常放通进行访问的。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值