package com.reyun.saas.web.config;

import com.reyun.saas.common.constants.PermitAllUrl;
import com.reyun.saas.security.hadler.Auth2ResponseExceptionTranslator;
import com.reyun.saas.security.hadler.SecurityAccessDeniedHandler;
import com.reyun.saas.security.hadler.SecurityAuthenticationEntryPoint;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;

@EnableResourceServer
@EnableWebSecurity
//spring security的注解  prePostEnabled = true表示controller层的 @PreAuthorize("hasAuthority('back:role:save')")开启注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
            ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint)
                    .setExceptionTranslator(new Auth2ResponseExceptionTranslator());
            resources.authenticationEntryPoint(authenticationEntryPoint);
        }

        /**
         * 为oauth2单独创建角色，这些角色只具有访问受限资源的权限，可解决token失效的问题
         * @param http
         * @throws Exception
         */
        @Override
        public void configure(HttpSecurity http) throws Exception {

            http.csrf().disable().cors().and().exceptionHandling()
                    .authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
                    .accessDeniedHandler(new SecurityAccessDeniedHandler())
                    .and().requestMatcher(new OAuth2RequestedMatcher()).authorizeRequests()
                    .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                    .antMatchers(PermitAllUrl.permitAllUrl()).permitAll() // 放开权限的url
                    .anyRequest().authenticated().and().httpBasic();
        }
        /**
         * 判断来源请求是否包含oauth2授权信息<br>
         * url参数中含有access_token,或者header里有Authorization
         */
        private static class OAuth2RequestedMatcher implements RequestMatcher {
            @Override
            public boolean matches(HttpServletRequest request) {
                if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
                    return true;
                }
                // 请求参数中包含access_token参数
                if (request.getParameter(OAuth2AccessToken.ACCESS_TOKEN) != null) {
                    return true;
                }
                // 头部的Authorization值以Bearer开头
                String auth = request.getHeader("Authorization");
                if (auth != null) {
                    return auth.startsWith(OAuth2AccessToken.BEARER_TYPE);
                }

                return false;
            }
        }
    }