JHipster Authentication with an External Service – 使用外部服务进行JHipster认证

最后修改: 2019年 8月 23日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

By default, JHipster applications use a local data store to hold usernames and passwords. In many real-world scenarios, however, it might be desirable to use an existing external service for authentication.

默认情况下,JHipster应用程序使用本地数据存储来保存用户名和密码。然而,在许多现实世界的场景中,使用现有的外部服务进行身份验证可能是可取的。

In this tutorial, we’ll look at how to use an external service for authentication in JHipster. This could be any well-known service such as LDAP, social login, or any arbitrary service that accepts a username and password.

在本教程中,我们将研究如何在JHipster中使用外部服务进行认证。这可以是任何知名的服务,如LDAP、社交登录,或任何接受用户名和密码的任意服务。

2. Authentication in JHipster

2.JHipster中的认证

JHipster uses Spring Security for authentication. The AuthenticationManager class is responsible for validating username and passwords.

JHipster使用Spring Security进行验证。AuthenticationManager类负责验证用户名和密码。

The default AuthenticationManager in JHipster simply checks the username and password against a local data store. This could be MySQL, PostgreSQL, MongoDB, or any of the alternatives that JHipster supports.

JHipster中默认的AuthenticationManager只是根据本地数据存储检查用户名和密码。这可能是MySQL、PostgreSQL、MongoDB或JHipster支持的任何替代方案。

It’s important to note that the AuthenticationManager is only used for initial login. Once a user has authenticated, they receive a JSON Web Token (JWT) that is used for subsequent API calls.

需要注意的是,AuthenticationManager只用于初始登录。一旦用户通过了认证,他们就会收到一个JSON网络令牌(JWT),用于后续的API调用。

2.1. Changing Authentication in JHipster

2.1.改变JHipster中的认证方式

But what if we already have a data store that contains usernames and passwords, or a service that performs authentication for us?

但是,如果我们已经有一个包含用户名和密码的数据存储,或者有一个为我们执行认证的服务,怎么办?

To provide a custom authentication scheme, we simply create a new bean of type AuthenticationManager. This will take precedence over the default implementation.

为了提供一个自定义的认证方案,我们只需创建一个新的AuthenticationManager类型的bean。这将优先于默认的实现。

Below is an example that shows how to create a custom AuthenticationManager. It only has one method to implement:

下面是一个例子,说明如何创建一个自定义的AuthenticationManager。它只有一个方法需要实现。

public class CustomAuthenticationManager implements AuthenticationManager {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        try {
            ResponseEntity<LoginResponse> response =
                restTemplate.postForEntity(REMOTE_LOGIN_URL, loginRequest, LoginResponse.class);
            
            if(response.getStatusCode().is2xxSuccessful()) {
                String login = authentication.getPrincipal().toString();
                User user = userService.getUserWithAuthoritiesByLogin(login)
                  .orElseGet(() -> userService.createUser(
                    createUserDTO(response.getBody(), authentication)));
                return createAuthentication(authentication, user);
            }
            else {
                throw new BadCredentialsException("Invalid username or password");
            }
        }
        catch (Exception e) {
            throw new AuthenticationServiceException("Failed to login", e);
        }
    }
}

In this example, we pass the username and credentials from the Authentication object to an external API.

在这个例子中,我们从Authentication对象中传递用户名和凭证到外部API。

If the call succeeds, we return a new UsernamePasswordAuthenticationToken to indicate success. Note that we also create a local user entry, which we’ll discuss later on.

如果调用成功,我们会返回一个新的UsernamePasswordAuthenticationToken来表示成功。注意,我们还创建了一个本地用户条目,我们将在后面讨论这个问题

If the call fails, we throw some variant of AuthenticationException so that Spring Security will gracefully fallback for us.

如果调用失败,我们会抛出某种变体AuthenticationException ,这样Spring Security就会优雅地为我们回退。

This example is intentionally simple to show the basics of custom authentication. However, it could perform more complex operations such as LDAP binding and authentication or use OAuth.

这个例子是故意简单的,以显示自定义验证的基本原理。但是,它可以执行更复杂的操作,如LDAP绑定和认证或使用OAuth

3. Other Considerations

3.其他考虑因素

Up until now, we’ve focused on the authentication flow in JHipster. But there are several other areas of our JHipster application we have to modify.

到目前为止,我们一直专注于JHipster中的认证流程。但是,在我们的JHipster应用程序中,还有其他几个方面我们必须要修改。

3.1. Front-End Code

3.1 前端代码

The default JHipster code implements the following user registration and activation process:

默认的JHipster代码实现了以下用户注册和激活过程。

  • A user signs up for an account using their email and other required details
  • JHipster creates an account and sets it as inactive and then sends an email to the new user with an activation link
  • Upon clicking the link, the user’s account is marked as active

There is a similar flow for password reset as well.

密码重置也有一个类似的流程。

These all make sense when JHipster is managing user accounts. But they are not required when we’re relying on an external service for authentication.

当JHipster管理用户账户时,这些都是有意义的。但当我们依靠外部服务进行认证时,就不需要这些了。

Therefore, we need to take steps to ensure these account management features are not accessible to the user.

因此,我们需要采取措施,确保这些账户管理功能不被用户访问

This means removing them from the Angular or React code, depending on which framework is being used in the JHipster application.

这意味着从Angular或React代码中删除它们,这取决于JHipster应用程序中使用的是哪种框架。

Using Angular as an example, the default login prompt includes links to password reset and registration. We should remove them from app/shared/login/login.component.html:

以Angular为例,默认的登录提示包括密码重置和注册的链接。我们应该从app/shared/login/login.component.html中删除它们。

<div class="alert alert-warning">
  <a class="alert-link" (click)="requestResetPassword()">Did you forget your password?</a>
</div>
<div class="alert alert-warning">
  <span>You don't have an account yet?</span>
   <a class="alert-link" (click)="register()">Register a new account</a>
</div>

We must also remove the unneeded navigation menu items from app/layouts/navbar/navbar.component.html:

我们还必须从app/layouts/navbar/navbar.component.html中删除不需要的导航菜单项。

<li *ngSwitchCase="true">
  <a class="dropdown-item" routerLink="password" routerLinkActive="active" (click)="collapseNavbar()">
    <fa-icon icon="clock" fixedWidth="true"></fa-icon>
    <span>Password</span>
  </a>
</li>

and

<li *ngSwitchCase="false">
  <a class="dropdown-item" routerLink="register" routerLinkActive="active" (click)="collapseNavbar()">
    <fa-icon icon="user-plus" fixedWidth="true"></fa-icon>
    <span>Register</span>
  </a>
</li>

Even though we removed all the links, a user could still manually navigate to these pages. The final step is to remove the unused Angular routes from app/account/account.route.ts.

即使我们删除了所有的链接,用户仍然可以手动导航到这些页面。最后一步是删除app/account/account.route.ts中未使用的Angular路由。

After doing this, only the settings route should remain:

这样做之后,应该只剩下设置路线。

import { settingsRoute } from './';
const ACCOUNT_ROUTES = [settingsRoute];

3.2. Java APIs

3.2 Java APIs

In most cases, simply removing the front-end account management code should be sufficient. However, to be absolutely sure the account management code is not invoked, we can also lock down the associated Java APIs.

在大多数情况下,只要删除前端的账户管理代码就足够了。然而,为了绝对确保账户管理代码不被调用,我们还可以锁定相关的Java APIs

The quickest way to do this is to update the SecurityConfiguration class to deny all requests to the associated URLs:

最快的方法是更新SecurityConfiguration类,拒绝所有对相关URL的请求。

.antMatchers("/api/register").denyAll()
.antMatchers("/api/activate").denyAll()
.antMatchers("/api/account/reset-password/init").denyAll()
.antMatchers("/api/account/reset-password/finish").denyAll()

This will prevent any remote access to the APIs, without having to remove any of the code.

这将防止对API的任何远程访问,而不需要删除任何代码。

3.3. Email Templates

3.3 电子邮件模板

JHipster applications come with a set of default email templates for account registration, activation, and password resets. The previous steps will effectively prevent the default emails from being sent, but in some cases, we might want to reuse them.

JHipster应用程序带有一套默认的电子邮件模板,用于账户注册、激活和密码重设。前面的步骤将有效地阻止默认电子邮件的发送,但在某些情况下,我们可能想重新使用它们。

For example, we might want to send a welcome email when a user logs in for the first time. The default template includes steps for account activation, so we have to modify it.

例如,我们可能想在用户第一次登录时发送一封欢迎邮件。默认模板包括账户激活的步骤,所以我们必须修改它。

All of the email templates are located in resources/templates/mail. They are HTML files that use Thymeleaf to pass data from Java code into the emails.

所有的电子邮件模板都位于resources/templates/mail中。它们是HTML文件,使用Thymeleaf来将数据从Java代码传递到电子邮件中。

All we have to do is to edit the template to include the desired text and layout and then use the MailService to send it.

我们所要做的就是编辑模板,包括所需的文本和布局,然后使用MailService来发送它。

3.4. Roles

3.4 角色

When we create the local JHipster user entry, we also have to take care to ensure it has at least one role. Normally, the default USER role is sufficient for new accounts.

当我们创建本地JHipster用户条目时,我们还必须注意确保它至少有一个角色。通常情况下,默认的USER角色对新账户来说是足够的。

If the external service provides its own role mapping, we have two additional steps:

如果外部服务提供它自己的角色映射,我们还有两个额外的步骤。

  1. Ensure any custom roles exist in JHipster
  2. Update our custom AuthenticationManager to set the custom roles when creating new users

JHipster also provides a management interface for adding and removing roles to users.

JHipster还提供了一个管理界面,用于向用户添加和删除角色。

3.5. Account Removal

3.5.帐户删除

It’s worth mentioning that JHipster also provides an account removal management view and API. This view is only available to administrator users.

值得一提的是,JHipster还提供了一个账户删除管理视图和API。这个视图只对管理员用户可用。

We could remove and restrict this code as we did for account registration and password reset, but it’s not really necessary. Our custom AuthenticationManager will always create a new account entry when someone logs in, so deleting the account doesn’t actually do much.

我们可以像对待账户注册和密码重置那样,删除和限制这段代码,但这其实是没有必要的。我们的自定义AuthenticationManager在有人登录时总是会创建一个新的账户条目,所以删除该账户实际上并没有什么作用。

4. Conclusion

4.总结

In this tutorial, we’ve seen how to replace the default JHipster authentication code with our own authentication scheme. This could be LDAP, OIDC, or any other service that accepts a username and password.

在本教程中,我们已经看到了如何用我们自己的认证方案替换默认的JHipster认证代码。这可以是LDAP、OIDC,或任何其他接受用户名和密码的服务。

We’ve also seen that using an external authentication service also requires some changes to other areas of our JHipster application. This includes front end views, APIs, and more.

我们还看到,使用外部认证服务也需要对我们的JHipster应用程序的其他区域进行一些改变。这包括前端视图、API,以及更多。

As always, the example code from this tutorial is available over on GitHub.

一如既往,本教程中的示例代码可在GitHub上获取。