简体   繁体   English

Spring - 远程基本认证

[英]Spring - Remote Basic Authentication

I'm rewriting a Monolith Java/Spring server to Microservices while exposing the same API interface to the clients so they won't notice any change was made.我正在将 Monolith Java/Spring 服务器重写为微服务,同时向客户端公开相同的 API 接口,因此他们不会注意到发生了任何更改。

In the Monolith server we use Spring-Security and Spring-Security-OAuth2 .在 Monolith 服务器中,我们使用Spring-SecuritySpring-Security-OAuth2

The first part is creating a Java based API-Gateway which will handle all authentication/authorization as a tunnel to the Monolith server.第一部分是创建一个基于 Java 的 API 网关,它将处理所有身份验证/授权作为通往 Monolith 服务器的隧道。

After creating a new microservice (using spring initializr) I have tried to configure spring security to tunnel all Authentication to the Monolith Server using:创建新的微服务后(使用 spring initializr),我尝试配置 spring 安全性以使用以下方式将所有身份验证隧道传输到 Monolith 服务器:

@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`
    }
}

This seems to be working as I visit the http://localhost:8080/login page of the spring-security, I can successfully login and the request is tunneled into our Monolith server.当我访问 spring-security 的http://localhost:8080/login页面时,这似乎有效,我可以成功登录,并且请求通过隧道传输到我们的 Monolith 服务器。

The problem starts when I try to configure the OAuth2 resource server, as our clients currently authenticating using a POST to oauth/token with some Basic Authentication in the Header:当我尝试配置OAuth2资源服务器时,问题就开始了,因为我们的客户端当前使用 Header 中的一些基本身份验证使用 POST 到oauth/token进行身份验证:

Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Note: The token for the basic is a static token for all clients (I know it's not worth anything but this is the way it's currently implementated and I try to implement a fully compatible API-Gateway)注意:基本的令牌是所有客户端的 static 令牌(我知道它没有任何价值,但这是目前实现的方式,我尝试实现完全兼容的 API 网关)

Which allows them to communicate with that end-point and get a valid token for the user/password in the body (Which is application/x-www-form-urlencoded):这允许他们与该端点进行通信并为正文中的用户/密码获取有效令牌(即 application/x-www-form-urlencoded):

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

The problem is that spring-security returning a 401 Unauthorized for that call without even letting the request enter the /oauth/login route and do a remote authentication.问题是 spring-security 为该调用返回401 Unauthorized ,甚至没有让请求进入/oauth/login路由并进行远程身份验证。

I can't find a way to tunnel the Basic Authentication into the monolith server so the /oauth/login will actually authenticate against the Monolith with Remote Basic Authentication and after success it will act as a tunnel and will pass the body itself into the Monolith /oauth/login end point (as I succesfully did in the WebSecurityConfig above)我找不到将基本身份验证隧道传输到单体服务器的方法,因此/oauth/login实际上将使用远程基本身份验证对单体进行身份验证,成功后它将充当隧道并将主体本身传递到单体/oauth/login端点(正如我在上面的WebSecurityConfig中成功完成的那样)

Any direction will be appreciated.任何方向将不胜感激。 Thanks in advance!提前致谢!

I am doing a similar project and I think that I can help you with some guidance.我正在做一个类似的项目,我认为我可以为您提供一些指导。

OAuth2 is token-based security authorization and authentication that we can break in four components: OAuth2 是基于令牌的安全授权和身份验证,我们可以分为四个组件:

  1. A Protected Resource (can be accessed only by the authenticated user that have the proper authorization)受保护的资源(只能由具有适当授权的经过身份验证的用户访问)
  2. A Resource Owner (defines what application can call their service which user are allowed to access the service and what can they do)资源所有者(定义哪些应用程序可以调用他们的服务,哪些用户可以访问该服务以及他们可以做什么)
  3. An Application (is the application that is going to call the service on behalf of the users)应用程序(是代表用户调用服务的应用程序)
  4. OAuth2 Authentication Service(stands between the application and the protected resource) OAuth2 身份验证服务(位于应用程序和受保护资源之间)

In your case, the protected resource is the monolith that you want to break in a microservices architecture.在您的情况下,受保护的资源是您想要在微服务架构中打破的单体。

  • The first thing you need to do is create an authorization service.您需要做的第一件事是创建授权服务。

Create SpringCloud project from spring intilizr make sure that the dependencies below are present:从 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>

After that in the main class of oauth2 service you need to add two annotation.之后,在 oauth2 服务的主要 class 中,您需要添加两个注释。 @EnableResourceServer is used to tell your microservice that is a protected resource. @EnableResourceServer用于告诉您的微服务是受保护的资源。 I will explain below why this is needed.我将在下面解释为什么需要这样做。 @EnableAuthorizationServer is going to tell spring cloud that is service is going to be used as OAuth2Service. @EnableAuthorizationServer将告诉 spring 云服务将用作 OAuth2Service。 Below it is the code snippet:下面是代码片段:

@SpringBootApplication
@EnableResourceServer
@EnableAuthorizationServer
public class Oauth2ServerApplication {

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

}

We create an endpoint that is going to exposes the user information.我们创建一个将公开用户信息的端点。 This endpoint is going to be called by other services.该端点将被其他服务调用。 This is the reason why we annotated this application with @EnableResourceServer .这就是我们使用@EnableResourceServer注释此应用程序的原因。 Below is the rest endpoint that return the user information:以下是返回用户信息的 rest 端点:

@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;
    }

}

Now you will register the application in oauth2 service.现在您将在 oauth2 服务中注册应用程序。 You will define the application that will access the protected resource.您将定义将访问受保护资源的应用程序。 You will create a configuration class that will define what application can use your service.您将创建一个配置 class 来定义哪些应用程序可以使用您的服务。

@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");
    }
}

You need to define what users and roles for the application.您需要定义应用程序的用户和角色。 This is familiar if you have done security with SpringBoot.如果您已经使用 SpringBoot 完成了安全性,这很熟悉。 Check the snippet below:检查下面的代码段:

@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");
    }
}

In the end check with postman if you can retrieve the token from oauth2 service.最后检查 postman 是否可以从 oauth2 服务检索令牌。

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

Authorization Type : Basic, username : Client id, password : client secret授权类型:基本,用户名:客户端 ID,密码:客户端密码

Body : form data,正文:表单数据,

grant:password授予:密码

scope:webclient scope:webclient

username: your username用户名:您的用户名

password: your password密码:您的密码

After retrieving the token, test if you can access that token the URL of the endpoint which you expose user information.检索令牌后,测试您是否可以访问该令牌,即您公开用户信息的端点的 URL。

HttpMethod : Get, URL : localhost:application-port/user HttpMethod :获取, URL :本地主机:应用程序端口/用户

Authorization : Bearer, Token : token generated Authorization : Bearer, Token : 生成的令牌


  • Second, protecting your old monolith which will be accessed from the gateway server.其次,保护将从网关服务器访问的旧单体。 Remember that your old monolith is a protected resource.请记住,您的旧单体是受保护的资源。

First you need to add Spring Security and OAuth2 jars to the service you are trying to protect.首先,您需要将 Spring Security 和 OAuth2 jars 添加到您要保护的服务中。

<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>

Next in the application.yml of the monolith we configure service point of your oauth2 service.接下来在单体应用程序的 application.yml 中,我们配置您的 oauth2 服务的服务点。 This is done because the monolith is a protected service and every time a request came you want to check if the token of the request is valid.这样做是因为单体应用是受保护的服务,每次收到请求时,您都想检查请求的令牌是否有效。

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

After that don't forget to add the @EnableResourceServer which makes the monolith a protected resource.之后不要忘记添加@EnableResourceServer ,这会使单体成为受保护的资源。


@SpringBootApplication
@EnableResourceServer
public class Application {

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

After that we specify the restriction we want.之后,我们指定我们想要的限制。 I have provided below with example restricting access to only authenticated users.我在下面提供了限制仅对经过身份验证的用户进行访问的示例。

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

  • Recap回顾

Your user is authenticated from OAuth service and has the generated token.您的用户已通过 OAuth 服务进行身份验证,并具有生成的令牌。 With the generated token send a request to monolith through the gateway server.使用生成的令牌通过网关服务器向单体发送请求。 The gateway tries to access monolith by propagating the token that it received from the request.网关尝试通过传播它从请求中收到的令牌来访问单体。 The monolith check if token it is valid.单体检查令牌是否有效。

Below is a link of my microservice architecture with zuul gateway server and oauth2 server: https://github.com/rshtishi/payroll .下面是我的微服务架构与zuul网关服务器和oauth2服务器的链接: https://github.com/rshtishi/payroll You can check it if you want to see more details.如果您想查看更多详细信息,可以检查它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM