SpringBoot开发Keycloak Spi实例

SpringBoot开发Keycloak Spi

  • 环境准备
  • SpringBoot准备
    • Client开发
    • Spi开发
      • 添加依赖
      • 添加maven部署插件
      • 添加配置
      • 代码开发
        • 实现Provider
        • 实现ProviderFactory
  • Keycloak准备工作
    • 部署
    • 配置文件
    • 启动Keycloak
  • 启动写好的Keycloak客户端项目
  • 测试

环境准备

keycloak10.0.1安装教程

SpringBoot准备

Client开发

具体开发参考:https://blog.csdn.net/m0_46267097/article/details/106092404

Spi开发

添加依赖

注意:依赖包的scope需要是provided

<dependency>
	<groupId>org.keycloakgroupId>
	<artifactId>keycloak-servicesartifactId>
	<version>10.0.1version>
	<scope>providedscope>
dependency>
<dependency>
	<groupId>org.keycloakgroupId>
	<artifactId>keycloak-coreartifactId>
    <version>10.0.1version>
    <scope>providedscope>
dependency>
    <dependency>
    <groupId>org.keycloakgroupId>
    <artifactId>keycloak-server-spiartifactId>
    <version>10.0.1version>
    <scope>providedscope>
dependency>

添加maven部署插件

需要去掉上面引用的几个keycloak的jar包,不然启动keycloak会无限报错!!!

<build>
<plugins>
    <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-compiler-pluginartifactId>
        <version>3.5.1version>
        <configuration>
            <source>1.8source>
            <target>1.8target>
        configuration>
    plugin>
    <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId> maven-assembly-plugin artifactId>
        <configuration>
            <descriptorRefs>
                <descriptorRef>jar-with-dependenciesdescriptorRef>
            descriptorRefs>
            <archive>
                <manifest>
                    <mainClass>mainClass>
                manifest>
                <addMavenDescriptor>falseaddMavenDescriptor>
            archive>
        configuration>
        <executions>
            <execution>
                <id>make-assemblyid>
                <phase>packagephase>
                <goals>
                    <goal>singlegoal>
                goals>
            execution>
        executions>
    plugin>
plugins>
<finalName>keycloak-spifinalName>
build>

添加配置

在resources目录下添加services文件夹,在services文件夹下新建文件,文件名为具体实现的工厂接口的名称,比如我这里实现的是UserStorageProviderFactory工厂,就新建一个文件名为UserStorageProviderFactory。文件内容为具体实现类的全限定名。

org.keycloak.examples.federation.properties.MyUserStorageProviderFactory

代码开发

实现Provider

package org.keycloak.examples.federation.properties;

import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialInputValidator;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.*;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.adapter.AbstractUserAdapter;
import org.keycloak.storage.user.UserLookupProvider;
import java.util.*;

/**
 * MyUserStorageProvider 
 *
 * @return
 * @exception 
 * @author : cy
 * @date : 2020/5/14
 */
public class MyUserStorageProvider implements UserStorageProvider,UserLookupProvider, CredentialInputValidator{
    protected KeycloakSession session;
    protected Properties properties;
    protected ComponentModel model;
    /** map of loaded users in this transaction */
    protected Map<String,UserModel> loadedUsers = new HashMap<>();

    public MyUserStorageProvider(KeycloakSession session, ComponentModel model, Properties properties) {
        this.session = session;
        this.model = model;
        this.properties = properties;
    }
	/**
	 * 创建用户模型
	 */
    protected UserModel createAdapter(RealmModel realm, String username) {
        return new AbstractUserAdapter(session, realm, model) {
            @Override
            public String getUsername() {
                return username;
            }
        };
    }
	/**
	 * 通过用户名查血用户
	 */
    @Override
    public UserModel getUserByUsername(String username, RealmModel realm) {
        UserModel adapter = loadedUsers.get(username);
        if (adapter == null) {
            String password = properties.getProperty(username);
            if (password != null) {
                adapter = createAdapter(realm, username);
                loadedUsers.put(username, adapter);
            }
        }
        return adapter;
    }
	/**
	 * 通过用户ID查血用户
	 */
    @Override
    public UserModel getUserById(String id, RealmModel realm) {
        StorageId storageId = new StorageId(id);
        String username = storageId.getExternalId();
        return getUserByUsername(username, realm);
    }

	/**
	 * 通过邮箱查血用户
	 */
    @Override
    public UserModel getUserByEmail(String email, RealmModel realm) {
        return null;
    }

    @Override
    public void close() {

    }
    /**
     * 运行时将调用该方法,以确定是否为用户配置了特定的凭据类型。此方法检查是否已为用户设置了密码
     */
    @Override
    public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
        String password = properties.getProperty(user.getUsername());
        return credentialType.equals(CredentialModel.PASSWORD) && password != null;
    }

    @Override
    public boolean supportsCredentialType(String credentialType) {
        return credentialType.equals(CredentialModel.PASSWORD);
    }
    /**
     * 方法负责验证密码。
     * 该CredentialInput参数实际上只是所有凭证类型的抽象接口。
     * 我们确保我们支持凭证类型,并且它也是的实例UserCredentialModel。
     * 当用户通过登录页面登录时,密码输入的纯文本将放入的实例UserCredentialModel。
     * 该isValid()方法根据存储在属性文件中的纯文本密码检查此值。返回值true表示密码有效。
     */
    @Override
    public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
        if (!supportsCredentialType(input.getType())) {
            return false;
        }

        String password = properties.getProperty(user.getUsername());
        if (password == null) {
            return false;
        }
        return password.equals(input.getChallengeResponse());
    }
}

实现ProviderFactory

package org.keycloak.examples.federation.properties;

import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.storage.UserStorageProviderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * MyUserStorageProviderFactory
 *
 * @return
 * @exception 
 * @author : cy
 * @date : 2020/5/14
 */
public class MyUserStorageProviderFactory
        implements UserStorageProviderFactory<MyUserStorageProvider> {

    public static final String PROVIDER_NAME = "my-user";

    @Override
    public String getId() {
        return PROVIDER_NAME;
    }

    private static final Logger logger = Logger.getLogger(MyUserStorageProviderFactory.class);
    protected Properties properties = new Properties();

    @Override
    public void init(Config.Scope config) {
        /**
         * 我们可以指定用户属性文件的类路径,而不是对其进行硬编码。然后,您可以在中检索配置
         */
        String path = config.get("path");
        InputStream is = getClass().getClassLoader().getResourceAsStream(path);
        if (is == null) {
            logger.warn("Could not find users.properties in classpath");
        } else {
            try {
                properties.load(is);
            } catch (IOException ex) {
                logger.error("Failed to load users.properties file", ex);
            }
        }
    }

    @Override
    public MyUserStorageProvider create(KeycloakSession session, ComponentModel model) {
        return new MyUserStorageProvider(session, model, properties);
    }
}


Keycloak准备工作

部署

使用上述maven-compiler-plugin的打包插件打包后,把jar包手动放置到keycloak-10.0.1\standalone\deployments目录下。

配置文件

修改keycloak-10.0.1\standalone\configuration\standalone.xml,添加如下配置,其中path位置输入的是自定义properties配置文件路径。

<spi name="storage">
<provider name="readonly-property-file" enabled="true">
    <properties>
    	
        <property name="path" value="/other-users.properties"/>
    properties>
provider>
spi>

properties文件内容如下

admin=123456

启动Keycloak

运行keycloak-10.0.1\bin\standalone.bat启动项目,登录进入Admin Console
左侧菜单栏选择User Federation后点击右侧下拉框,可以看见我们加入的Provider出现了。
SpringBoot开发Keycloak Spi实例_第1张图片

选择后进入其配置页面SpringBoot开发Keycloak Spi实例_第2张图片
通过这种方法获得的用户角色名为offline_access,有了角色名,也有了数据来源,后期我们的用户验证就可以开始为所欲为喽。

启动写好的Keycloak客户端项目

具体开发参考Client开发,或者再来个链接:SpringBoot集成Keycloak简单实例
不过其中配置需要稍稍修改一下,需要把原来的authRoles里面的角色改成offline_access,一定要改,不然你会发现登了半天进去的永远是报错页面。
直接贴全配置:

spring:
  application:
    name: keycloakDemo
server:
  port: 8600
keycloak:
  # 表示是一个public的client
  public-client: true
  # keycloak的地址
  auth-server-url: http://localhost:8080/auth
  # keycloak中的realm
  realm: myrealm
  # client ID
  resource: keycloakDemo
  # 安全约束
  securityConstraints:
    - authRoles:
        # 以下路径需要demoUser角色才能访问
        - offline_access
      securityCollections:
        # name可以随便写
        - name: common user
          patterns:
            - /demo/getValue

测试

输入测试地址http://localhost:8600/demo/getValue
SpringBoot开发Keycloak Spi实例_第3张图片
输入配置文件中的用户名、密码后点击登录按钮,成功。
SpringBoot开发Keycloak Spi实例_第4张图片
开发参考链接:官网SPI开发文档

你可能感兴趣的:(开发实例)