美文网首页Springcloud快速入门SpringCloudSpringCloud
三、SpringCloud鉴权之OAuth2.0(上篇)

三、SpringCloud鉴权之OAuth2.0(上篇)

作者: 阿亮私语 | 来源:发表于2019-07-14 15:02 被阅读25次

    鉴权中心

    springsecurity +oauth2.0

    1、前言

    必备知识

    学习本文之前你应该会熟练使用Springboot,并对SpringSecurityOAuth2.0有所理解,如有需要请参考下面的一些内容,简单理解下相关知识

    SpringSecurity

    Spring Security是一个功能强大、高度可定制的身份验证和访问控制框架。它用于保护基于Spring的应用程序。Spring Security是一个专注于向Java应用程序提供身份验证和授权的框架。
    与所有Spring项目一样,Spring安全的真正威力在于它可以很容易地扩展以满足定制需求。

    OAuth2.0

    OAuth 2.0是用于授权的行业标准协议。OAuth2.0注重客户端开发人员的简单性,同时为Web应用程序、桌面应用程序、移动电话和客厅设备提供特定的授权流。
    更多请参考 OAuth2.0

    OAuth2.0模式

    模式 应用场景 描述
    授权码(Auth Code)
    简化模式(implicit)
    密码模式(password credentials)
    客户端模式(client credentials)

    JWT

    JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。更多请参考 JWT入门教程

    2、前期必备

    核心pom依赖如下:

    <!-- 注意是starter,自动配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- 不是starter,手动配置 -->
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.3.6.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 将token存储在redis中 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    

    创建一个rest接口用于后面测试资源

    @Slf4j
    @RestController
    public class TestSecurityController {
        @GetMapping("/product/{id}")
        public String getProduct(@PathVariable String id) {
            //for debug
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            return "product id : " + id;
        }
    
        @GetMapping("/order/{id}")
        public String getOrder(@PathVariable String id) {
            //for debug
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            return "order id : " + id;
        }
    }
    

    3、操作步骤

    很多文章写的特别复杂,其实主要的内容也就分为下面几步

    3.1、授权服务器AuthorizationServerConfigurerAdapter

    需要自定义授权服务器,继承AuthorizationServerConfigurerAdapter,详细代码如下

    @Configuration
    @EnableAuthorizationServer
    public   class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
        private static final String DEMO_RESOURCE_ID = "order";
    
        @Autowired
        AuthenticationManager authenticationManager;
        @Autowired
        RedisConnectionFactory redisConnectionFactory;
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //配置两个客户端,一个用于password认证一个用于client认证
            String secret = new BCryptPasswordEncoder().encode("123456");////对密码进行加密
            clients.inMemory().withClient("client_1")
                    .resourceIds(DEMO_RESOURCE_ID)
                    .authorizedGrantTypes("client_credentials", "refresh_token")
                    .scopes("select")
                    .authorities("client")
                    .secret(secret)
                    .and().withClient("client_2")
                    .resourceIds(DEMO_RESOURCE_ID)
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("select")
                    .authorities("client")
                    .secret(secret);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .tokenStore(new RedisTokenStore(redisConnectionFactory))
                    .authenticationManager(authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            //允许表单认证
            oauthServer.allowFormAuthenticationForClients();
        }
    
    }
    

    3.2、资源服务器ResourceServerConfigurerAdapter

    同上,需要实现自己的资源服务器,继承ResourceServerConfigurerAdapter,详细代码如下

    @Configuration
    @EnableResourceServer
    public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        private static final String DEMO_RESOURCE_ID = "order";
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                    // Since we want the protected resources to be accessible in the UI as well we need
                    // session creation to be allowed (it's disabled by default in 2.0.6)
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .requestMatchers().anyRequest()
                    .and()
                    .anonymous()
                    .and()
                    .authorizeRequests()
    //                    .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')")
                    .antMatchers("/order/**").authenticated();//配置order访问控制,必须认证过后才可以访问
            // @formatter:on
        }
    }
    

    3.3、配置SpringSecurity

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Bean
        @Override
        protected UserDetailsService userDetailsService(){
            InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            String pwd = new BCryptPasswordEncoder().encode("123456");//对密码进行加密
            manager.createUser(User.withUsername("user_1").password(pwd).authorities("USER").build());
            manager.createUser(User.withUsername("user_2").password(pwd).authorities("USER").build());
            return manager;
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers().anyRequest()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/oauth/*").permitAll();
        }
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            AuthenticationManager manager = super.authenticationManagerBean();
            return manager;
        }
    
        @Bean
        PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    }
    

    4、测试

    我们设计的是product服务可以匿名访问,而order服务需要签名才可以访问,验证如下:

    • password模式
      利用postman进行post访问http://localhost:8080/oauth/token?username=user_1&password=123456&grant_type=password&scope=select&client_id=client_2&client_secret=123456
      获取如下结果
    {
        "access_token": "c2340190-48f3-4291-bb17-1e4d51bcb284",
        "token_type": "bearer",
        "refresh_token": "03ee113c-a942-452a-9918-7ffe24472a7f",
        "expires_in": 40399,
        "scope": "select"
    }
    
    image
    • client模式
      同样利用postman的POST方式访问http://localhost:8080/oauth/token?grant_type=client_credentials&scope=select&client_id=client_1&client_secret=123456
      结果如下
    {
        "access_token": "05a4e614-f34b-4c83-9ec1-89ea55c0afd2",
        "token_type": "bearer",
        "expires_in": 40396,
        "scope": "select"
    }
    
    
    image
    对资源进行访问
    • product服务:访问http://localhost:8080/product/1得到如下数据
      product id : 1
    • order服务:访问http://localhost:8080/order/1,返回数据如下
    <oauth>
    <error_description>
    Full authentication is required to access this resource
    </error_description>
    <error>unauthorized</error>
    </oauth>
    

    验证结果,说明order服务需要签名才可以访问,接下来,我们输入签名访问order服务。
    我们分别利用上面password模式获取的token,访问 http://localhost:8080/order/1?access_token=c2340190-48f3-4291-bb17-1e4d51bcb284
    得到数据 order id : 1

    通用利用client模式获取的token,访问 http://localhost:8080/order/1?access_token=05a4e614-f34b-4c83-9ec1-89ea55c0afd2
    同样可以得到 order id : 1

    示例代码

    分支为springsecurity-oauth2-1,master为最终代码

    https://github.com/liangliang1259/fast-cloud-examples/tree/springsecurity-oauth2-1

    参考

    相关文章

      网友评论

        本文标题:三、SpringCloud鉴权之OAuth2.0(上篇)

        本文链接:https://www.haomeiwen.com/subject/ytobkctx.html