[英]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-Security
和Spring-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 是基于令牌的安全授权和身份验证,我们可以分为四个组件:
In your case, the protected resource is the monolith that you want to break in a microservices architecture.在您的情况下,受保护的资源是您想要在微服务架构中打破的单体。
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, URL : http://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 : 生成的令牌
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();
}
}
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.