Project: Apollo (Provisioning / Automation & Configuration)
File: portal-how-to-implement-user-login-function.md

Apollo is a configuration management system and will provide authority management (Authorization), theoretically it is not responsible for the implementation of user login authentication function (Authentication). So Apollo defines some SPI's for decoupling and the key to Apollo access login is to implement these SPI's. Apollo provides a simple authentication version of Http Basic using Spring Security since 0.9.0 for this situation. Use the following steps. If the previous version is 0.8.0, you need to import Checking ApolloPortalDB, the `Users` table should already exist and have an initial record. The initial user name is apollo and the password is admin. | Id | Username | Password | Email | Enabled | | - | -- | | | - | | 1 | apollo | $2a$10$7r20uS.BQ9uBpf3Baj3uQOZvMVvB1RN3PYoKE94gtz2.WAOuiiwXS | apollo@acme.com | 1 | Make sure `-Dapollo_profile=github,auth` if it is started by IDE Super Administrator can add users by opening `Administrator Tools - User Management` after logging into the system. After logging in, open `Administrator Tools - User Management` and enter the user name and password to change the user's password, we recommend changing the password of super administrator apollo at the same time. Starting from version 1.2.0, Apollo supports ldap protocol login, which can be configured as follows. If you use helm chart deployment method, it is recommended to implement it by configuration method without modifying the image, you can refer to After unpacking `apollo-portal-x.x.x-github.zip`, create `application-ldap.yml` in the `config` directory with the following reference () , the relevant content needs to be adjusted according to the specific situation: ```yml spring: ldap: base: "dc=example,dc=org" username: "cn=admin,dc=example,dc=org" # Configure administrator account for searching and matching users password: "password" searchFilter: "(uid={0})" # user filter, use this filter to search for users when logging in urls: "ldap://localhost:389" ldap: mapping: # Configure the ldap attribute objectClass: "inetOrgPerson" # ldap user objectClass configuration loginId: "uid" # ldap user unique id, used as the login id userDisplayName: "cn" # ldap user name, used as display name email: "mail" # ldap mailbox attribute ``` With OpenLDAP , the filter can be configured to narrow down the users to search for. ```yml spring: ldap: base: "dc=example,dc=org" username: "cn=admin,dc=example,dc=org" # Configure admin account for searching and matching users password: "password" searchFilter: "(uid={0})" # user filter, use this filter to search for users when logging in urls: "ldap://localhost:389" ldap: mapping: # Configure the ldap attribute objectClass: "inetOrgPerson" # ldap user objectClass configuration loginId: "uid" # ldap user unique id, used as the login id userDisplayName: "cn" # ldap user name, used as display name email: "mail" # ldap mailbox attribute filter: # Configuration filter, currently only memberOf is supported memberOf: "cn=ServiceDEV,ou=DEV,dc=example,dc=org|cn=WebDEV,ou=DEV,dc=example,dc=org" # Only allow users with memberOf attribute of ServiceDEV and WebDEV to access ``` Starting with version 1.3.0, we support filtering users based on Group, which allows you to control that only users of a specific Group can log in and use apollo. ```yml spring: ldap: base: "dc=example,dc=org" username:

# Configure admin account for searching and matching users password: "password" searchFilter: "(uid={0})" # user filter, use this filter to search for users when logging in urls: "ldap://localhost:389" ldap: mapping: # Configure the ldap attribute objectClass: "inetOrgPerson" # ldap user objectClass configuration loginId: "uid" # ldap user unique id, used as login id rdnKey: "uid" # ldap rdn key userDisplayName: "cn" # ldap user name, used as display name email: "mail" # ldap mailbox attribute group: # enable group search, only users of a specific group can login to apollo after enabling objectClass: "posixGroup" # Configure groupClassName groupBase: "ou=group" # group search base groupSearch: "(&(cn=dev))" # group filter groupMembership: "memberUid" # group memberShip eg. member or memberUid ``` Modify `scripts/startup.sh` to specify `spring.profiles.active` as `github,ldap`. ```bash SERVICE_NAME=apollo-portal LOG_DIR=/opt/logs SERVER_PORT=8070 export JAVAOPTS="$JAVAOPTS -Dspring.profiles.active=github,ldap" ``` After unpacking `apollo-portal-x.x.x-github.zip`, create `application-ldap.yml` in the `config` directory with the following reference () , the relevant content needs to be adapted to the specific case: ```yml spring: ldap: base: "dc=example,dc=com" username: "admin" # Configure administrator account for searching and matching users password: "password" searchFilter: "(sAMAccountName={0})" # user filter, use this filter to search for users when logging in urls: "ldap://1.1.1.1:389" ldap: mapping: # Configure the ldap attribute objectClass: "user" # ldap user objectClass configuration loginId: "sAMAccountName" # the unique id of the ldap user, used as the login id userDisplayName: "cn" # ldap user name, used as display name email: "userPrincipalName" # ldap mailbox attribute filter: # optional, configure filter, currently only support memberOf memberOf: "CN=ServiceDEV,OU=test,DC=example,DC=com|CN=WebDEV,OU=test,DC=example,DC=com" # Only allow users with memberOf attribute of ServiceDEV and WebDEV to access ``` Modify ``scripts/startup.sh`` to specify ``spring.profiles.active`` as ``github,ldap``. ```bash SERVICE_NAME=apollo-portal LOG_DIR=/opt/logs SERVER_PORT=8070 export JAVAOPTS="$JAVAOPTS -Dspring.profiles.active=github,ldap" ``` After unpacking `apollo-portal-x.x.x-github.zip`, create `application-ldap.yml` in the `config` directory with the following reference () , the relevant content needs to be adjusted according to the specific situation: ```yml spring: ldap: base: "dc=example,dc=com" username: "uid=admin,ou=system" # Configure administrator account for searching and matching users password: "password" searchFilter: "(uid={0})" # user filter, use this filter to search for users when logging in urls: "ldap://localhost:10389" ldap: mapping: # Configure the ldap attribute objectClass: "inetOrgPerson" # ldap user objectClass configuration loginId: "uid" # ldap user unique id, used as the login id userDisplayName: "displayName" # ldap user name, used as display name email: "mail" # ldap mailbox attribute ``` Starting with version 1.3.0, we support filtering users based on Group, which allows you to control that only users of a specific Group can log in and use apollo. ```yml spring: ldap: base: "dc=example,dc=com" username: "uid=admin,ou=system" # Configure admin account for searching and matching users password: "password" searchFilter: "(uid={0})" # user filter, use this filter to search for users when logging in urls: "ldap://localhost:10389" ldap: mapping: # Configure the ldap attribute objectClass: "inetOrgPerson" # ldap user objectClass configuration loginId: "uid" # ldap user unique id, used as the login id rdnKey: "cn" # ldap rdn key userDisplayName: "displayName" # ldap user name, used as display name email: "mail" # ldap mailbox attribute

# Configure ldap group, only users of specific group can login apollo after enabled objectClass: "groupOfNames" # Configure groupClassName groupBase: "ou=group" # group search base groupSearch: "(&(cn=dev))" # group filter groupMembership: "member" # group memberShip eg. member or memberUid ``` Modify ``scripts/startup.sh`` to specify ``spring.profiles.active`` as ``github,ldap``. ```bash SERVICE_NAME=apollo-portal LOG_DIR=/opt/logs SERVER_PORT=8070 export JAVAOPTS="$JAVAOPTS -Dspring.profiles.active=github,ldap" ``` Since version 1.8.0 OpenID Connect login is supported, this implementation requires that the OpenID Connect login service has been deployed Before configuration, you need to prepare: OpenID Connect provider configuration endpoint (RFC 8414-compliant issuer-uri), which needs to be https*, e.g. https://host:port/auth/realms/apollo/.well-known/openid-configuration Create a client in the OpenID Connect service, the signature algorithm of the idToken must be set to RS256*, get the client-id and the corresponding client-secret After unpacking `apollo-portal-x.x.x-github.zip`, create `application-oidc.yml` in the `config` directory with the following contents () , the relevant content needs to be adapted to the specific case: ```yml server: forward-headers-strategy: framework spring: security: oauth2: client: provider: <fill-in-the-provider-name-here>: issuer-uri: https://host:port/auth/realms/apollo registration: <fill-in-the-registration-name-here>: authorization-grant-type: authorization_code client-authentication-method: basic client-id: apollo-portal provider: <fill-in-the-provider-name-here> scope: openid client-secret: d43c91c0-xxxx-xxxx-xxxx-xxxxxxxxxxxx ``` If the OpenID Connect login service supports clientcredentials mode, you can also configure a registration of type clientcredentials, which can be used by apollo-portal as a client to request other resources protected by oidc If the OpenID Connect login service supports jwt, you can also configure ${spring.security.oauth2.resourceserver.jwt.issuer-uri} to support accessing apollo-portal via jwt ```yml server: forward-headers-strategy: framework spring: security: oauth2: client: provider: <fill-in-the-provider-name-here>: issuer-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri} registration: <fill-in-the-registration-name-here>: authorization-grant-type: authorization_code client-authentication-method: basic client-id: apollo-portal provider: <fill-in-the-provider-name-here> scope: openid client-secret: d43c91c0-xxxx-xxxx-xxxx-xxxxxxxxxxxx registration-name-client: authorization-grant-type: client_credentials client-authentication-method: basic client-id: apollo-portal provider: <fill-in-the-provider-name-here> scope: openid client-secret: ${spring.security.oauth2.client.registration.registration-name.client-secret} resourceserver: jwt: issuer-uri: https://host:port/auth/realms/apollo ``` you can also configure a custom user display name in the `application-oidc.yml` see https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims for standard oidc claim name, and see your OpenID Connect service manager or ISP for nonstandard claim name. the configuration property name for oidc (interactive) user display name is `spring.security.oidc.user-display-name-claim-name`, default `preferredusername`, and fallback to `name` if the claim value of `preferredusername` is blank the configuration property name for oidc jwt user display name is `spring.security.oidc.jwt-user-display-name-claim-name`, has no default. for example, using `name` as the claim of oidc (interactive) user display name. ```yml spring: security: oidc: user-display-name-claim-name: "name" ``` for example, using `email` as the claim of oidc (interactive) user display name. ```yml spring: security: oidc: user-display-name-claim-name: "email" ``` There is no claim name suitable for user display name in jwt standard claim name (https://tools.ietf.org/html/rfc7519#section-4), see your OpenID Connect service manager or ISP for a nonstandard claim name for display. for example, using `userdisplayname` as the claim of oidc jwt user display name. ```yml spring: security: oidc: jwt-user-display-name-claim-name: "userdisplayname" ``` it's ok to configure oidc (interactive) user display name and oidc jwt user display name at the same time. for example, using `name` as the claim of oidc (interactive) user display name and using `userdisplayname` as the claim of oidc jwt user display name. ```yml spring: security: oidc: user-display-name-claim-name: "name" jwt-user-display-name-claim-name: "userdisplayname" ``` Modify ``scripts/startup.sh`` to specify

as ``github,oidc``. ```bash SERVICE_NAME=apollo-portal LOG_DIR=/opt/logs SERVER_PORT=8070 export JAVAOPTS="$JAVAOPTS -Dspring.profiles.active=github,oidc" ``` Using nginx as an example, add or include (recommended) the following configuration directly to the http configuration section of nginx ```nginx server { listen 80 default_server; location / { return 301 https://$httphost$requesturi; } } server { listen 443 ssl http2; server_name xxx; ssl_certificate /etc/nginx/ssl/xxx.crt; sslcertificatekey /etc/nginx/ssl/xxx.key; location / { proxy_pass http://apollo-portal-dev:8070; proxysetheader x-real-ip $remote_addr; proxysetheader x-forwarded-for $proxyaddxforwardedfor; proxysetheader host $http_host; proxysetheader x-forwarded-proto $scheme; proxyhttpversion 1.1; } } ``` The configuration item ``server.forward-headers-strategy=framework`` must exist in ``application-oidc.yml``. ```yml server: forward-headers-strategy: framework ``` For security reasons, the OpenID Connect login service generally has a whitelist of redirected addresses, so you need to add the apollo-portal https address to the whitelist in order to redirect properly. This approach assumes that the company already has a unified login authentication system, such as SSO and LDAP. The following SPI must be implemented to access the system, including UserService and UserInfoHolder. The interfaces are described as follows. UserService (Required): User service, used to provide user search-related functions to the Portal UserInfoHolder (Required): Get the current login user information, SSO is generally the current login user information on ThreadLocal LogoutHandler (Optional): used to implement the logout function SsoHeartbeatHandler (Optional): If the Portal page is not refreshed for a long time, the login information will expire. Refresh the login information through this interface You can refer to `apollo-portal` module's package of , under this package for four implementations. defaultimpl: default implementation, with only one global apollo account ctrip: ctrip implementation, access to the SSO and the implementation of the user search, query interface springsecurity: spring security implementation, you can add new users, modify the user password, etc. ldap: ldap implementation contributed by and After implementing the relevant interfaces, the AuthConfiguration can be accessed via to replace the default implementation at runtime. The idea of accessing SSO is as follows. SSO will provide a jar package, you need to configure a filter filter will intercept all requests and check if you are already logged in if there is no login, then it will jump to the SSO login page After successful login in the SSO login page, it will jump back to the apollo page with the authentication information. 5. enter the SSO filter again, verify the authentication information, save the user's information, and write the user's credentials to a cookie or distributed session to avoid having to log in again next time enter Apollo's code, Apollo's code will call UserInfoHolder.getUser to get the current logged-in user Note that the above steps 1-5 are all SSO code, not Apollo code, Apollo code only requires you to implement step 6. Note: The runtime use of different implementations is achieved by . For example, if your own sso implementation is in the `custom` profile, you can specify -Dapollo_profile=github,custom in the packaging script. profile. Also note that in to modify the default implementation of the condition , from `@ConditionalOnMissingProfile({"ctrip", "auth", "ldap"})` to `@ConditionalOnMissingProfile({"ctrip", "auth", "ldap", "custom"})`.