简体   繁体   English

在 JAR 文件中添加外部 jar 依赖项

[英]Adding external jar dependency in JAR file

Backgorund : I am trying to add UserStorageSPI for my legacy application, so that we can use existing user credentials to log in. I have followed this tutorial , and the full source code for the sample application is available here . Backgorund:我正在尝试为我的旧应用程序添加 UserStorageSPI,以便我们可以使用现有的用户凭据登录。我已按照本教程进行操作,示例应用程序的完整源代码可在此处获得。

This application is storing plaintext passwords and compares them directly.此应用程序存储明文密码并直接比较它们。 However, my legacy database stores the passwords in an encrypted format.但是,我的旧数据库以加密格式存储密码。 To test a sample user, I stored the credentials in bcrypt format and I overrode the following method (isValid(...)), in my CustomUserStorageProvider class.为了测试示例用户,我以 bcrypt 格式存储了凭据,并在我的 CustomUserStorageProvider 类中覆盖了以下方法 (isValid(...))。

Approach 1:方法一:

public class CustomUserStorageProvider implements UserStorageProvider, 
      UserLookupProvider,
      CredentialInputValidator,
      UserQueryProvider {
    
      ....
    
     @Override
        public boolean isValid(RealmModel realm, UserModel user, CredentialInput credentialInput) {
            log.info("[I57] isValid(realm={},user={},credentialInput.type={})",realm.getName(), user.getUsername(), credentialInput.getType());
            if( !this.supportsCredentialType(credentialInput.getType())) {
                return false;
            }
            StorageId sid = new StorageId(user.getId());
            String username = sid.getExternalId();
            
            try ( Connection c = DbUtil.getConnection(this.model)) {
    
                log.info("Username to query :: " + username);
                PreparedStatement st = c.prepareStatement("select password from users where username = ?");
                st.setString(1, username);
                st.execute();
                ResultSet rs = st.getResultSet();
                log.info("RS " + rs);
                if ( rs.next()) {
                    String pwd = rs.getString(1);
    
                    log.info("Password coming from query :: " + pwd);
                    log.info("Password coming from user input :: " + credentialInput.getChallengeResponse());
    

PasswordEncoder enc = new BCryptPasswordEncoder();
                    log.info(" enc.matches(pwd, credentialInput.getChallengeResponse()) " +  enc.matches(pwd, credentialInput.getChallengeResponse()));
                    return  enc.matches(pwd, credentialInput.getChallengeResponse());
                }
                else {
                    return false;
                }
            }
            catch(SQLException ex) {
                throw new RuntimeException("Database error:" + ex.getMessage(),ex);
            }
        }
    
    }

The keycloak can read the custom provider when I put the jar in the relevant path.当我将 jar 放在相关路径中时,keycloak 可以读取自定义提供程序。 I can also see keycloak is showing all the imported users from this new database, however, I get the following exception in keycloak logs, while the code is trying to compare the passwords.我还可以看到 keycloak 正在显示来自这个新数据库的所有导入用户,但是,我在 keycloak 日志中得到以下异常,而代码正在尝试比较密码。

2022-06-24 15:53:30,566 INFO  [com.test.spi.provider.CustomUserStorageProvider] (executor-thread-2) RS org.postgresql.jdbc.PgResultSet@75dadacb
2022-06-24 15:53:30,566 INFO  [com.test.spi.provider.CustomUserStorageProvider] (executor-thread-2) Password coming from query :: $2a$12$rrtl/vDlCCF0cK0aKu5H6uW30B4fp9cIrTQlHPMayQTR9ToZicEoW
2022-06-24 15:53:30,566 INFO  [com.test.spi.provider.CustomUserStorageProvider] (executor-thread-2) Password coming from user input :: test
2022-06-24 15:53:30,567 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-2) Uncaught server error: java.lang.NoClassDefFoundError: org/springframework/security/crypto/password/PasswordEncoder
        at com.test.spi.provider.CustomUserStorageProvider.isValid(CustomUserStorageProvider.java:147)
        at org.keycloak.credential.UserCredentialStoreManager.lambda$validate$4(UserCredentialStoreManager.java:164)
        at java.base/java.util.Collection.removeIf(Collection.java:576)
        at org.keycloak.credential.UserCredentialStoreManager.validate(UserCredentialStoreManager.java:164)
        at org.keycloak.credential.UserCredentialStoreManager.isValid(UserCredentialStoreManager.java:151)
        at org.keycloak.credential.UserCredentialStoreManager.isValid(UserCredentialStoreManager.java:110)
        at org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator.validatePassword(AbstractUsernameFormAuthenticator.java:229)
        at org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator.validateUserAndPassword(AbstractUsernameFormAuthenticator.java:150)
        at org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.validateForm(UsernamePasswordForm.java:55)
        at org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.action(UsernamePasswordForm.java:48)
        at org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:169)
        at org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:990)
        at org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:321)
        at org.keycloak.services.resources.LoginActionsService.processAuthentication(LoginActionsService.java:292)
        at org.keycloak.services.resources.LoginActionsService.authenticate(LoginActionsService.java:276)
        at org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:349)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:71)
        at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
        at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.ClassNotFoundException: org.springframework.security.crypto.password.PasswordEncoder
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        at io.quarkus.bootstrap.runner.RunnerClassLoader.loadClass(RunnerClassLoader.java:107)
        at io.quarkus.bootstrap.runner.RunnerClassLoader.loadClass(RunnerClassLoader.java:57)
        ... 65 more

I also tried adding PasswordEncoder as Bean in startup application and added it as Autowired dependency in CustomUserStorageProvider class but I get the same exception.我还尝试在启动应用程序中将 PasswordEncoder 添加为 Bean,并将其添加为 CustomUserStorageProvider 类中的 Autowired 依赖项,但我得到了同样的异常。

Approach 2 :方法2:

@SpringBootApplication
public class SpiApplication {

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

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

}

-----
@Service
public class CustomUserStorageProvider implements UserStorageProvider, 
  UserLookupProvider,
  CredentialInputValidator,
  UserQueryProvider {

    @Autowired
    PasswordEncoder enc ;

My pom.xml dependencies are here :我的 pom.xml 依赖项在这里:

<dependencies>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-crypto</artifactId>
</dependency>

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-core</artifactId>
    <version>18.0.0</version>
    
</dependency>

<!-- User Storage SPI dependency -->
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi</artifactId>
    <version>18.0.0</version>
    </dependency>

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi</artifactId>
    <version>18.0.0</version>
</dependency>
<build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.2.4</version>
                </plugin>
            </plugins>
        </pluginManagement>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>

<shadedArtifactAttached>true</shadedArtifactAttached>
                    </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

I am using this command to generate the jar file and keep it inside keycloak-18.0.0/providers/ package.我正在使用此命令生成 jar 文件并将其保存在 keycloak-18.0.0/providers/ 包中。

mvn package

Please assist me with the correct approach.请用正确的方法帮助我。 I have gone through several blog posts and questions on this forum as well, but have not obtained the proper solution.我也在这个论坛上浏览了几篇博客文章和问题,但没有得到正确的解决方案。 TIA. TIA。

Main issue: I even setup this project in eclipse and tried exporting the project as jar file (runnable jar/jar both).主要问题:我什至在 Eclipse 中设置了这个项目,并尝试将项目导出为 jar 文件(可运行的 jar/jar 两者)。 The far jar generated is huge in size.生成的far jar 很大。 32 MBs instead of 16KBs created by shade plugin but the exception remains the same.阴影插件创建的 32 MB 而不是 16 KB,但例外情况保持不变。 Why is this dependency not getting added in the classpath of the jar?为什么这个依赖没有被添加到 jar 的类路径中? I cannot make the 3rd party application using this jar add the classpath, so it has to be embedded inside the final jar.我不能让使用这个 jar 的 3rd 方应用程序添加类路径,所以它必须嵌入到最终的 jar 中。 Please help.请帮忙。 I have spend a lot of time and I am running out to new workarounds to even try.我已经花了很多时间,并且正在尝试新的解决方法。

Thankfully I found this post and it worked for me.谢天谢地,我找到了这篇文章,它对我有用。

https://github.com/keycloak/keycloak/issues/10230 https://github.com/keycloak/keycloak/issues/10230

To summarize,总结一下,

  1. Add scope as provided for keycloak, login, Postgres, or any dependencies which keycloak might already have.provided 、login、Postgres 或 keycloak 可能已经拥有的任何依赖项添加范围。 This is how my updated maven looks like,这就是我更新后的 Maven 的样子,

    org.keycloak keycloak-server-spi ${keycloak.version} provided org.postgresql postgresql 42.3.6 provided org.keycloak keycloak-server-spi ${keycloak.version} 提供 org.postgresql postgresql 42.3.6 提供
    <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>5.7.1</version> </dependency>
  2. Utilize the maven assembly plugin and execute it using the below command.利用 Maven 程序集插件并使用以下命令执行它。 Helpful post for that another answer . 另一个答案的有用帖子。 Command : mvn clean compile assembly:single命令: mvn clean compile assembly:single

Plugin :插入 :

 <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <finalName>my-user-provider</finalName>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
        </plugin>
    </plugins>
</build>

Above 2 modifications, repaired my thing.以上2次修改,修复了我的东西。

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

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