package com.reyun.saas.web.config;

import com.reyun.saas.security.service.RedisAuthorizationCodeServices;
import com.reyun.saas.security.service.RedisClientDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;
        @Autowired
        private RedisClientDetailsService redisClientDetailsService;
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
        @Autowired
        private RedisAuthorizationCodeServices redisAuthorizationCodeServices;
        @Value("${token.oneAccount.onlyOne:false}")
        private boolean onlyOne;
        /**
         * ClientDetails实现
         *
         * @return
         */
        /*@Bean
        public ClientDetailsService clientDetails() {
            return new JdbcClientDetailsService(dataSource);
        }*/

        @Bean
        public TokenStore tokenStore() {
            RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
            /**
             * 同已用户每次获取token，获取到的都是同一个token，只有token失效后才会获取新token。
             * 同一用户每次获取token都生成一个完成周期的token并且保证每次生成的token都能够使用（多点登录）。
             * 同一用户每次获取token都保证只有最后一个token能够使用，之前的token都设为无效（单点token）。
             * 这里使用第二种：参考https://www.cnblogs.com/cq-yangzhou/p/13207069.html
             */
            if (!onlyOne){
                redisTokenStore.setAuthenticationKeyGenerator(new MyAuthenticationKeyGenerator());
            }
            return redisTokenStore;
        }


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // 1. 数据库的方式
            //clients.withClientDetails(clientDetails());
            // 2.优化，详细看下redisClientDetailsService这个实现类
            clients.withClientDetails(redisClientDetailsService);
            redisClientDetailsService.loadAllClientToCache();
        }

        /**
         * 声明授权和token的端点以及token的服务的一些配置信息，
         * 比如采用什么存储方式、token的有效期等
         * @param endpoints
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
            endpoints.authenticationManager(this.authenticationManager);
            endpoints.tokenStore(tokenStore());
            // 授权码模式下，code存储
//		    endpoints.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource));
            endpoints.authorizationCodeServices(redisAuthorizationCodeServices);
        }


        /**
         * 声明安全约束，哪些允许访问，哪些不允许访问
         * @param oauthServer
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
            oauthServer
                    .tokenKeyAccess("permitAll()")
                    //.checkTokenAccess("permitAll()")
                    .allowFormAuthenticationForClients();
        }
    }