All Downloads are FREE. Search and download functionalities are using the official Maven repository.

matrix.business.oauth2.config.ResourceServerAutoConfiguration Maven / Gradle / Ivy

package matrix.business.oauth2.config;

import lombok.SneakyThrows;
import matrix.boot.based.bean.IgnoreAuthBean;
import matrix.boot.common.dto.Result;
import matrix.boot.common.exception.ServiceException;
import matrix.boot.common.utils.AssertUtil;
import matrix.boot.common.utils.StringUtil;
import matrix.business.oauth2.core.ExcludeRequestMatcher;
import matrix.business.oauth2.properties.OAuth2Properties;
import matrix.business.oauth2.service.OAuthFilterService;
import matrix.business.oauth2.service.impl.DefaultOAuthFilterServiceImpl;
import matrix.business.oauth2.service.impl.DefaultRemoteTokenServiceImpl;
import matrix.business.oauth2.service.impl.DefaultClientDetailServiceImpl;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import matrix.boot.based.utils.WebUtil;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 资源服务器配置
 *
 * @author wangcheng
 * 2021/8/27
 **/
@ConditionalOnProperty(value = {"matrix.business.oauth2.enabled", "matrix.business.oauth2.resource-server.enabled"})
@Configuration
@EnableConfigurationProperties({OAuth2Properties.class, OAuth2Properties.AuthorizationServerProperties.class, OAuth2Properties.ResourceServerProperties.class})
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerAutoConfiguration extends ResourceServerConfigurerAdapter {

    /**
     * 默认不用资源认证的链接
     */
    private static final List NOT_RESOURCE_VALIDATE_URLS = Arrays.asList("/oauth/**", "/login", "/error");

    /**
     * oauth2配置
     */
    private final OAuth2Properties oAuth2Properties;

    @Autowired(required = false)
    private RestTemplate restTemplate;

    @Autowired
    private OAuth2WebSecurityExpressionHandler expressionHandler;

    /**
     * 忽略验证的beans
     */
    @Autowired
    private ObjectProvider ignoreAuthBeans;

    public ResourceServerAutoConfiguration(OAuth2Properties oAuth2Properties) {
        this.oAuth2Properties = oAuth2Properties;
    }

    /**
     * 当前资源服务器的一些配置, 如资源服务器ID
     *
     * @param resources 资源信息
     */
    @SneakyThrows
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        //认证服务配置
        OAuth2Properties.AuthorizationServerProperties authorizationServerProperties = oAuth2Properties.getAuthorizationServer();
        //资源服务配置
        OAuth2Properties.ResourceServerProperties resourceServerProperties = oAuth2Properties.getResourceServer();
        //参数校验
        AssertUtil.notNullTip(resourceServerProperties.getResourceId(), "matrix.business.oauth2.resource-server.resource-id");
        //资源服务Token校验服务
        ResourceServerTokenServices resourceServerTokenServices;
        if (authorizationServerProperties != null && authorizationServerProperties.isEnabled()) {
            //认证服务器也打开,代表本服务即是认证服务器也是资源服务器
            resourceServerTokenServices = new DefaultTokenServices();
            ((DefaultTokenServices) resourceServerTokenServices).setTokenStore(WebUtil.getBean(TokenStore.class));
            ((DefaultTokenServices) resourceServerTokenServices).setAuthenticationManager(WebUtil.getBean(AuthenticationManager.class));
            if (authorizationServerProperties.getClientDetailsClass() != null) {
                try {
                    ((DefaultTokenServices) resourceServerTokenServices).setClientDetailsService(authorizationServerProperties.getClientDetailsClass().newInstance());
                } catch (Exception e) {
                    throw new ServiceException(e);
                }
            } else {
                //使用默认的客户端
                ((DefaultTokenServices) resourceServerTokenServices).setClientDetailsService(new DefaultClientDetailServiceImpl());
            }
            ((DefaultTokenServices) resourceServerTokenServices).setAccessTokenValiditySeconds(oAuth2Properties.getAuthorizationServer().getDefaultAccessTokenValiditySeconds());
            ((DefaultTokenServices) resourceServerTokenServices).setRefreshTokenValiditySeconds(oAuth2Properties.getAuthorizationServer().getDefaultRefreshTokenValiditySeconds());
            ((DefaultTokenServices) resourceServerTokenServices).setSupportRefreshToken(true);
        } else {
            //参数校验
            AssertUtil.notNullTip(resourceServerProperties.getAuthServerUrl(), "matrix.business.oauth2.resource-server.auth-server-url");
            AssertUtil.notNullTip(resourceServerProperties.getClientId(), "matrix.business.oauth2.resource-server.client-id");
            AssertUtil.notNullTip(resourceServerProperties.getClientSecret(), "matrix.business.oauth2.resource-server.client-secret");
            //本服务只是单纯的资源服务
            resourceServerTokenServices = new DefaultRemoteTokenServiceImpl(Boolean.TRUE.equals(resourceServerProperties.getInternalAuthServer()) ? restTemplate : null);
            //请求认证服务器验证URL
            ((DefaultRemoteTokenServiceImpl) resourceServerTokenServices).setAuthServerUrl(resourceServerProperties.getAuthServerUrl());
            //设置是否jwt模式
            ((DefaultRemoteTokenServiceImpl) resourceServerTokenServices).setJwtLocalParse(resourceServerProperties.isJwtLocalParse());
            //在认证服务器配置的客户端id
            ((DefaultRemoteTokenServiceImpl) resourceServerTokenServices).setClientId(resourceServerProperties.getClientId());
            //在认证服务器配置的客户端密码
            ((DefaultRemoteTokenServiceImpl) resourceServerTokenServices).setClientSecret(resourceServerProperties.getClientSecret());
            //设置TOKEN转换器
            if (resourceServerProperties.getJwtAccessTokenConverterClass() != null) {
                ((DefaultRemoteTokenServiceImpl) resourceServerTokenServices).setAccessTokenConverter(resourceServerProperties.getJwtAccessTokenConverterClass().newInstance());
            }
        }
        //配置当前资源服务器的ID
        resources.resourceId(oAuth2Properties.getResourceServer().getResourceId()).tokenServices(resourceServerTokenServices);
        //表达式解析器
        resources.expressionHandler(expressionHandler);
        //设置auth进入点
        resources.authenticationEntryPoint(new DefaultOAuth2AuthenticationEntryPoint());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //资源服务配置
        OAuth2Properties.ResourceServerProperties resourceServerProperties = oAuth2Properties.getResourceServer();
        if (resourceServerProperties.getHttpSecurityClass() != null) {
            resourceServerProperties.getHttpSecurityClass().newInstance().init(http);
        } else {
            //忽略的uri
            Set ignoreAuthUris = new HashSet<>();
            //获取黑白名单过滤服务类
            OAuthFilterService oAuthFilterService;
            if (oAuth2Properties.getAuthorizationServer() != null) {
                if (!CollectionUtils.isEmpty(oAuth2Properties.getAuthorizationServer().getIgnoreAuthUris())) {
                    ignoreAuthUris.addAll(oAuth2Properties.getAuthorizationServer().getIgnoreAuthUris());
                }
                oAuthFilterService = oAuth2Properties.getAuthorizationServer().getOauthFilterClass() == null
                        ? new DefaultOAuthFilterServiceImpl()
                        : oAuth2Properties.getAuthorizationServer().getOauthFilterClass().newInstance();
            } else {
                oAuthFilterService = oAuth2Properties.getResourceServer().getOauthFilterClass() == null
                        ? new DefaultOAuthFilterServiceImpl()
                        : oAuth2Properties.getResourceServer().getOauthFilterClass().newInstance();
            }
            if (!CollectionUtils.isEmpty(oAuth2Properties.getResourceServer().getIgnoreAuthUris())) {
                ignoreAuthUris.addAll(oAuth2Properties.getResourceServer().getIgnoreAuthUris());
            }
            //默认忽略头验证的urls
            if (!CollectionUtils.isEmpty(NOT_RESOURCE_VALIDATE_URLS)) {
                ignoreAuthUris.addAll(NOT_RESOURCE_VALIDATE_URLS);
            }
            //忽略的验证链接
            List ignoreAuthBeanList = ignoreAuthBeans.orderedStream().collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(ignoreAuthBeanList)) {
                ignoreAuthUris.addAll(ignoreAuthBeanList.stream().flatMap(item -> item.getIgnoreResourceUris().stream()).collect(Collectors.toList()));
            }
            //排除链接配置
            ExcludeRequestMatcher excludeRequestMatcher = new ExcludeRequestMatcher(ignoreAuthUris, null, oAuthFilterService);
            if (CollectionUtils.isEmpty(resourceServerProperties.getSecurityConfig())) {
                //默认配置
                http.requestMatcher(new OrRequestMatcher(excludeRequestMatcher)).authorizeRequests().anyRequest().authenticated();
            } else {
                //用户配置解析
                List securityConfigs = resourceServerProperties.getSecurityConfig();
                List requestMatchers = new ArrayList<>();
                requestMatchers.add(excludeRequestMatcher);
                securityConfigs.forEach(securityConfig -> {
                    Assert.state(!StringUtil.isEmpty(securityConfig.getUris()), "oauth2 security config uris not be null");
                    for (String uri : securityConfig.getUris().split(",")) {
                        requestMatchers.add(new AntPathRequestMatcher(uri));
                    }
                });
                ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http.requestMatcher(new OrRequestMatcher(requestMatchers)).authorizeRequests();
                for (OAuth2Properties.SecurityConfig securityConfig : securityConfigs) {
                    String[] uris = securityConfig.getUris().split(",");
                    if (!StringUtil.isEmpty(securityConfig.getClientScopes())) {
                        //判断范围
                        registry = registry.antMatchers(uris).access("#oauth2.hasAnyScope(" + securityConfig.getClientScopes() + ")");
                    } else if (!StringUtil.isEmpty(securityConfig.getClientRoles())) {
                        //判断角色
                        registry = registry.antMatchers(uris).access("#oauth2.clientHasAnyRole(" + securityConfig.getClientRoles() + ")");
                    } else {
                        registry = registry.antMatchers(uris).authenticated();
                    }
                }
                registry.and().csrf().disable().cors().disable();
            }
        }
    }

    /**
     * 表达式解析器
     *
     * @param applicationContext 上下文
     * @return 表达式解析器
     */
    @Bean
    public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
        OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }

    /**
     * 默认验证进入点
     */
    public static class DefaultOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {

        @Override
        protected ResponseEntity enhanceResponse(ResponseEntity response, Exception exception) {
            ResponseEntity responseEntity = super.enhanceResponse(response, exception);
            return new ResponseEntity(Result.fail(exception.getMessage(), responseEntity.getStatusCodeValue()),
                    responseEntity.getHeaders(), HttpStatus.OK);
        }
    }

}