Spring Boot使用客户端ID和客户端密码通过基本身份验证公开REST API

时间:2020-04-01 12:53:49

标签: java spring spring-boot spring-security spring-security-oauth2

在我的应用程序中,我既有休息也有网络部分。
Web部件的URL模式为/admin/**,它使用基于表单的身份验证。
而其余部分的网址格式为/api/**,它使用jwt令牌进行身份验证。
同样在默认配置下,还有另一个网址格式/oauth/*,即/oauth/token , /oauth/token_key etc
我正在尝试公开专用于/open/api/**的rest api,该api使用/oauth/token中的基本身份验证。 这样,对/open/api/**的任何请求都看起来像

POST http://{{host}}/open/api/test
  Accept: application/json
  Authorization: Basic {base64encoded(clientId:clientSecret)} // this is important to expose the api
  cache-control: no-cache

我尝试过google,但找不到任何配置。
我的配置是


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MultiHttpSecurityConfig {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Configuration
    @Order(1)
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        Logger logger = LoggerFactory.getLogger(FormLoginWebSecurityConfigurerAdapter.class);

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http
                .antMatcher("/admin/**")
                .authorizeRequests()
                    .anyRequest().hasAnyAuthority("ADMIN_USER")
                .and()
                    .formLogin()
                    .loginPage("/admin/login")
                    .permitAll()
                .and()
                    .logout()
                        .logoutUrl("/admin/logout")
                        .invalidateHttpSession(true)
                        .permitAll()
                .and()
                        .exceptionHandling()
                        .accessDeniedPage("/403");

            http.csrf().disable();

            http.headers().frameOptions().disable();

        }

        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
        
    }

    @Configuration
    @EnableResourceServer
    @Order(2)
    public class CustomResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

        Logger logger = LoggerFactory.getLogger(CustomResourceServerConfigurerAdapter.class);

        @Autowired
        private JdbcTemplate jdbcTemplate;

        @Bean
        public TokenStore tokenStore() {
            return new JdbcTokenStore(jdbcTemplate.getDataSource());
        }

        @Bean
        @Primary
        //Making this primary to avoid any accidental duplication with another token service instance of the same name
        public DefaultTokenServices tokenServices() {
            DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            defaultTokenServices.setTokenStore(tokenStore());
            defaultTokenServices.setSupportRefreshToken(true);
            return defaultTokenServices;
        }



        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenServices(tokenServices());
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {

            http
                .antMatcher("/api/**")
                .authorizeRequests()
                    .antMatchers("/api/**" ).authenticated();

        }
    }

}

是否可以在此配置中配置/open/api,还是必须编写自己的实现才能实现?

编辑。...

经过大量尝试,想出了添加过​​滤器的方法,如下所示。它有效,但我不知道它是否是正确的方法。欢迎提出建议。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Component;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class OpenApiFilter implements Filter {

    Logger logger = LoggerFactory.getLogger(OpenApiFilter.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private ClientDetailsServiceConfigurer clients;

    @Autowired
    private PasswordEncoder encoder;

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        logger.info("Initializing filter :{}", this);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

                HttpServletRequest req = (HttpServletRequest) request;
                HttpServletResponse httpResponse = (HttpServletResponse) response;

                String auth = req.getHeader(HttpHeaders.AUTHORIZATION);
                
                if(auth == null ){

                    httpResponse.setContentType("application/json");
                    httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
                    
                    return;

                }else{

                    if(!auth.startsWith("Basic ")){

                        httpResponse.setContentType("application/json");
                        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

                        return;

                    }else{

                        auth = auth.substring("Basic ".length());
                        if(!Base64.isBase64(auth)){

                            httpResponse.setContentType("application/json");
                            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

                            return;

                        }else{

                            byte[] decoded = Base64.decodeBase64(auth);
                            auth = new String(decoded, "UTF-8");

                            if( !(auth.indexOf(":") > 1) ){

                                httpResponse.setContentType("application/json");
                                httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
                                
                                return;

                            }else{

                                String[] credentials = auth.split(":");

                                try {

                                    ClientDetailsService jdbcClientDetailsServiceBuilder = clients.jdbc(jdbcTemplate.getDataSource()).build();

                                    ClientDetails clientDetails =  jdbcClientDetailsServiceBuilder.loadClientByClientId(credentials[0]);

                                    if(!encoder.matches(credentials[1], clientDetails.getClientSecret())){

                                        httpResponse.setContentType("application/json");
                                        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");   

                                        return;

                                    }
                                    
                                } catch (Exception e) {

                                    logger.error("{}", e.getMessage());

                                    httpResponse.setContentType("application/json");
                                    httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");

                                    return;
                                }

                            }

                        }

                    }

                }

                chain.doFilter(request, response);

    }
    
    @Override
    public void destroy() {
        logger.info("Destructing filter :{}", this);
    }
}

并已在bean中注册

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

@Configuration
public class OpenApiFilterConfig {

    @Autowired
    private OpenApiFilter openApiFilter;

    @Bean
    public FilterRegistrationBean < OpenApiFilter > filterRegistrationBean() {
     FilterRegistrationBean < OpenApiFilter > registrationBean = new FilterRegistrationBean<>();
   
     registrationBean.setFilter(openApiFilter);
     registrationBean.addUrlPatterns("/open/api/*");
     registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); //set precedence
     return registrationBean;

    }

}

0 个答案:

没有答案