package ink.wgink.login.oauth2.server.config;

import ink.wgink.login.base.service.user.UserDetailServiceImpl;
import ink.wgink.login.oauth2.server.converter.UserAccessTokenConverter;
import ink.wgink.login.oauth2.server.generator.OAuth2AuthorizationCodeGranter;
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientDetailsServiceImpl;
import ink.wgink.login.oauth2.server.service.impl.OAuth2ClientTokenServiceImpl;
import ink.wgink.service.user.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: OAuth2AuthorizationServerConfig
 * @Description: OAuth2认证服务器配置
 * @Author: wanggeng
 * @Date: 2021/9/15 4:54 下午
 * @Version: 1.0
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private UserDetailServiceImpl userDetailService;
    @Autowired
    private IUserService userService;
    @Autowired
    private OAuth2ClientDetailsServiceImpl oAuth2ClientDetailsService;
    @Autowired
    private OAuth2ClientTokenServiceImpl oAuth2ClientTokenService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 通过业务代码完成认证
        clients.withClientDetails(oAuth2ClientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        // 标识可以全部操作
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 添加JWT授权机制
        endpoints
                .pathMapping("/oauth/authorize", "/oauth2_client/authorize")
                .pathMapping("/oauth/token", "/oauth2_client/token")
                .pathMapping("/oauth/token_key", "/oauth2_client/token_key")
                .pathMapping("/oauth/check_token", "/oauth2_client/check_token")
                .pathMapping("/oauth/confirm_access", "/oauth2_client/confirm_access")
                .pathMapping("/oauth/error", "/oauth2_client/error")
                .authenticationManager(authenticationManager)
                .tokenStore(jwtTokenStore())
                .accessTokenConverter(jwtAccessTokenConverter())
                .userDetailsService(userDetailService);
    }

    @Bean(name = "jwtTokenStore")
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean(name = "jwtAccessTokenConverter")
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        // 添加自定义的认证机制，用来将自定义登陆后客户端拿到的信息
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setAccessTokenConverter(new UserAccessTokenConverter(userService));
        jwtAccessTokenConverter.setSigningKey("WGINK");
        return jwtAccessTokenConverter;
    }

    @Bean
    public TokenGranter tokenGranter() {
        return new TokenGranter() {
            private CompositeTokenGranter delegate;

            @Override
            public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
                if (delegate == null) {
                    delegate = new CompositeTokenGranter(getDefaultTokenGranters());
                }
                return delegate.grant(grantType, tokenRequest);
            }
        };
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }

    private List<TokenGranter> getDefaultTokenGranters() {
        AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
        OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(oAuth2ClientDetailsService);
        List<TokenGranter> tokenGranters = new ArrayList<>();
        // token
        tokenGranters.add(new OAuth2AuthorizationCodeGranter(oAuth2ClientTokenService, authorizationCodeServices, oAuth2ClientDetailsService, requestFactory));
        tokenGranters.add(new RefreshTokenGranter(oAuth2ClientTokenService, oAuth2ClientDetailsService, requestFactory));
        ImplicitTokenGranter implicit = new ImplicitTokenGranter(oAuth2ClientTokenService, oAuth2ClientDetailsService, requestFactory);
        tokenGranters.add(implicit);
        tokenGranters.add(new ClientCredentialsTokenGranter(oAuth2ClientTokenService, oAuth2ClientDetailsService, requestFactory));
        if (authenticationManager != null) {
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, oAuth2ClientTokenService, oAuth2ClientDetailsService, requestFactory));
        }
        return tokenGranters;
    }

    @Bean
    public OAuth2RequestFactory oAuth2RequestFactory() {
        OAuth2RequestFactory oAuth2RequestFactory = new DefaultOAuth2RequestFactory(oAuth2ClientDetailsService);
        return oAuth2RequestFactory;
    }

    @Bean
    public UserApprovalHandler userApprovalHandler() {
        TokenStoreUserApprovalHandler tokenStoreUserApprovalHandler = new TokenStoreUserApprovalHandler();
        tokenStoreUserApprovalHandler.setClientDetailsService(oAuth2ClientDetailsService);
        tokenStoreUserApprovalHandler.setTokenStore(jwtTokenStore());
        tokenStoreUserApprovalHandler.setRequestFactory(oAuth2RequestFactory());
        return tokenStoreUserApprovalHandler;
    }

    public static void main(String[] args) {
        PasswordEncoder pe = new BCryptPasswordEncoder();
        System.out.println(pe.encode("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA=="));
//        boolean matches = pe.matches("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA==", "$2a$10$.IjNieJzGGpC0Vv3gjIWB.Zjj1pi9VZS8M96zbbv3qj8gDsYX./h6");
        boolean matches = pe.matches("dGdIYVJvaEwvdFovRG01REtyYmVuMi9xMXVzR0lIYmxoNmdSZ3R6MWE4WGxIdG9KZmEyTjJIRnI0dG1McEdEVA==", "$2a$10$nopzmRLR8HneFyw2uccG9u3XNnK4Cupzb4BR0m/WzSGucB2Rsxwxq");
        System.out.println(matches);

    }

}
