java - Spring-远程基本身份验证

标签 java spring authentication spring-security spring-security-oauth2

我正在将Monolith Java / Spring服务器重写为微服务,同时向客户端公开相同的API接口,这样他们就不会注意到所做的任何更改。

在Monolith服务器中,我们使用Spring-SecuritySpring-Security-OAuth2

第一部分是创建一个基于Java的API-网关,它将处理所有身份验证/授权,作为通往Monolith服务器的隧道。

创建新的微服务后(使用spring initializr),我尝试使用以下方法配置spring安全性以将所有身份验证隧道传输到Monolith Server:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final AuthenticationProvider authenticationProvider;

    public WebSecurityConfig(AuthenticationProvider authenticationProvider) {
        this.authenticationProvider = authenticationProvider;
    }
}


@Component("authenticationProvider")
public class CustomRemoteAuthenticationProvider extends RemoteAuthenticationProvider {

    @Override
    @Autowired
    @Qualifier("remoteAuthenticationManager")
    public void setRemoteAuthenticationManager(RemoteAuthenticationManager remoteAuthenticationManager) {
        super.setRemoteAuthenticationManager(remoteAuthenticationManager);
    }

}


@Service("remoteAuthenticationManager")
public class CustomRemoteAuthenticationManager implements RemoteAuthenticationManager {

    @Override
    public Collection<? extends GrantedAuthority> attemptAuthentication(String username, String password) throws RemoteAuthenticationException {
        ... here I do an HTTP call to the Monolith `/oauth/login`
    }
}


当我访问spring-security的http://localhost:8080/login页面时,这似乎正在工作,我可以成功登录并将请求传输到我们的Monolith服务器中。

当我尝试配置OAuth2资源服务器时,问题就开始了,因为我们的客户端当前正在使用POST对oauth/token进行身份验证,并且标题中包含一些基本身份验证:

Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


注意:基本令牌是所有客户端的静态令牌(我知道它不值钱,但是这是当前实现的方式,我尝试实现完全兼容的API网关)

这使他们能够与该端点进行通信,并为正文中的用户/密码获取有效的令牌(这是application / x-www-form-urlencoded):

password=somepassword&username=user@example.com&grant_type=password&scope=read%20write


问题是spring-security为该调用返回了401 Unauthorized,甚至没有让请求进入/oauth/login路由并进行远程身份验证。

我找不到将基本身份验证隧道传输到Monolith服务器中的方法,因此/oauth/login实际上将通过远程基本身份验证针对Monolith进行身份验证,成功后它将充当隧道并将主体本身传递到Monolith < cc>终点(就像我在上面的/oauth/login中所做的那样)

任何方向将不胜感激。
提前致谢!

最佳答案

我正在做一个类似的项目,我认为我可以为您提供一些指导。

OAuth2是基于令牌的安全授权和身份验证,我们可以将其分为四个部分:


受保护的资源(只能由具有适当授权的经过身份验证的用户访问)
资源所有者(定义哪些应用程序可以调用其服务,允许哪些用户访问该服务以及他们可以做什么)
应用程序(是代表用户调用服务的应用程序)
OAuth2身份验证服务(位于应用程序和受保护的资源之间)


在您的情况下,受保护的资源就是您要在微服务体系结构中打破的整体。


您需要做的第一件事是创建授权服务。


从spring intilizr创建SpringCloud项目,确保存在以下依赖项:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>


之后,在oauth2服务的主类中,您需要添加两个注释。 @EnableResourceServer用于告诉您的微服务这是受保护的资源。我将在下面解释为什么需要这样做。 @EnableAuthorizationServer告诉Spring Cloud,该服务将用作OAuth2Service。下面是代码片段:

@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
public class Oauth2ServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(Oauth2ServerApplication.class, args);
    }

}


我们创建一个将公开用户信息的端点。该端点将被其他服务调用。这就是为什么我们用@EnableResourceServer注释此应用程序的原因。以下是返回用户信息的其余端点:

@RestController
@RequestMapping("/user")
public class UserRestController {

    @GetMapping(produces="application/json")
    public Map<String,Object> getUser(OAuth2Authentication user){
        Map<String,Object> userInfo = new HashMap<>();
        userInfo.put("user",user.getUserAuthentication().getPrincipal());
        userInfo.put("authorities", user.getUserAuthentication().getAuthorities());
        return userInfo;
    }

}


现在,您将在oauth2服务中注册该应用程序。您将定义将访问受保护资源的应用程序。您将创建一个配置类,该类将定义哪些应用程序可以使用您的服务。

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    OAuth2ConfigParameters oauth2ConfigParameters;

    @Bean
    public OAuth2ConfigParameters oAuth2ConfigParameters() {
        return new OAuth2ConfigParameters();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
        .withClient("your application")
        .secret("your password")
        .authorizedGrantTypes( "refresh_token","password","client_credentials")
        .scopes("webclient","mobileclient");
    }
}


您需要定义应用程序的用户和角色。如果您已经使用SpringBoot完成了安全性,这是很熟悉的。检查以下代码段:

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

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

    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }

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

    @Override 
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
        .withUser("you user")
        .password(passwordEncoder().encode("your password"))
        .roles("your role");
    }
}


最后,请与邮递员联系,以确认是否可以从oauth2服务中检索令牌。

HttpMethod:POST,URL:http://localhost:application-port/oauth/token

授权类型:基本,用户名:客户端ID,密码:客户端密钥

正文:表单数据,

授予:密码

范围:webclient

用户名:您的用户名

密码:您的密码

检索令牌后,测试是否可以访问公开用户信息的端点的URL。

HttpMethod:获取,URL:localhost:application-port / user

授权:不记名,令牌:生成令牌




其次,保护将要从网关服务器访问的旧整体。请记住,您的旧巨石是受保护的资源。


首先,您需要将Spring Security和OAuth2 jar添加到您要保护的服务中。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>


接下来,在整体的application.yml中,我们配置您的oauth2服务的服务点。这样做是因为整体是受保护的服务,并且每次请求到来时,您都要检查请求的令牌是否有效。

security:
 oauth2:
  resource:
   userInfoUri: http://localhost:oauth2-app-port/auth/user


在那之后,别忘了添加@EnableResourceServer,使整装成为受保护的资源。


@SpringBootApplication
@EnableResourceServer
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}


之后,我们指定所需的限制。我在下面提供了将访问限制为仅通过身份验证的用户的示例。

@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }
}





回顾


您的用户已通过OAuth服务进行了身份验证,并具有生成的令牌。使用生成的令牌,通过网关服务器将请求发送给整体。网关尝试通过传播从请求中收到的令牌来访问整体。整体检查令牌是否有效。

以下是我的微服务架构与zuul网关服务器和oauth2服务器的链接:https://github.com/rshtishi/payroll
如果您想查看更多详细信息,可以对其进行检查。

关于java - Spring-远程基本身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61139608/

相关文章:

spring - Spring WebFlux 中的 ReactiveSecurityContextHolder 为空

java - 在同一个项目中使用 spring mvc 应用程序和 jersey 是否存在潜在问题?

facebook - iOS 6 facebook 身份验证应用程序状态

JAVA反对二维数组,不能设置数组中的元素,也不能调用数组中的函数

java - 如何过滤流映射期间抛出异常的元素

java - 即使抛出 NullPointerException 后,Spring Batch 指数重试也不会启动

javascript - ASP.NET MVC。检查用户是否获得 JavaScript 授权

web-services - RESTful Web 服务 - 如何验证来自其他服务的请求?

java - Struts 2应用程序抛出404异常

java - 在方法内部本地访问静态最终字段的测试方法给出错误