SpringBoot开发Keycloak Spi实例_springboot集成keycloak spi方式-程序员宅基地

技术标签: 开发实例  

环境准备

keycloak10.0.1安装教程

SpringBoot准备

Client开发

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

Spi开发

添加依赖

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

<dependency>
	<groupId>org.keycloak</groupId>
	<artifactId>keycloak-services</artifactId>
	<version>10.0.1</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>org.keycloak</groupId>
	<artifactId>keycloak-core</artifactId>
    <version>10.0.1</version>
    <scope>provided</scope>
</dependency>
    <dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi</artifactId>
    <version>10.0.1</version>
    <scope>provided</scope>
</dependency>

添加maven部署插件

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

<build>
<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId> maven-assembly-plugin </artifactId>
        <configuration>
            <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
            <archive>
                <manifest>
                    <mainClass></mainClass>
                </manifest>
                <addMavenDescriptor>false</addMavenDescriptor>
            </archive>
        </configuration>
        <executions>
            <execution>
                <id>make-assembly</id>
                <phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>
<finalName>keycloak-spi</finalName>
</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>
    	<!-- 这里配置的是工厂模式中加载的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出现了。
在这里插入图片描述

选择后进入其配置页面在这里插入图片描述
通过这种方法获得的用户角色名为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
在这里插入图片描述
输入配置文件中的用户名、密码后点击登录按钮,成功。
在这里插入图片描述
开发参考链接:官网SPI开发文档

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_46267097/article/details/106123857

智能推荐

Sample Probability Space_probability space例题-程序员宅基地

文章浏览阅读66次。Sample Probability SpaceA simple probability space consist of a tuple (Ω\OmegaΩ,ε\varepsilonε,p)Ω\OmegaΩ is a finite set (with cardinality k= ∣\mid∣Ω\OmegaΩ∣\mid∣)ε\varepsilonε = { A : A ⊆\subseteq⊆ Ω\OmegaΩ } consist of all finite subsetsEach events _probability space例题

python分布式服务系统框架_Cola:一个分布式爬虫框架 - 系统架构 - Python4cn(news, jobs)...-程序员宅基地

文章浏览阅读348次。由于早先写的WeiboCrawler问题很多,而且当时我有提到,其实可以实现一个通用的爬虫框架。最近由于要抓取新的数据,于是我就写了这个cola。下面的文字来自wiki。Cola是一个分布式的爬虫框架,用户只需编写几个特定的函数,而无需关注分布式运行的细节。任务会自动分配到多台机器上,整个过程对用户是透明的。依赖由于Cola配置文件使用的yaml,所以Cola只依赖于pyyaml,安装easy_i..._cola爬虫

名悦集团国庆出行自驾游攻略-程序员宅基地

文章浏览阅读75次。在这个买车已经不是什么难事的年代,大多数人出行都会选择自驾方式,但自驾出游必然要面临一系列的问题以及做足准备工作。名悦集团小编给大家总结出了这次国庆假期出行自驾游攻略,为了保证自驾过程的安全顺利,玩得更加痛快,临行前的车辆检查时必不可少的,这些项目可以自己检查,如果实在是懒或者不懂,可以在临行前去4S店做个基础保养,以让爱车在最佳车况陪伴自己和家人朋友开始这段愉快的旅程。1.轮胎轮胎的检查是自驾前需要关键的检查之一,在自驾的旅程上不管是轮胎没气还是爆胎,都是非常令人揪心的,如果是在高速或者...

Android 输入法框架源码分析总结(1)_android 输入法源码-程序员宅基地

文章浏览阅读3.6k次,点赞3次,收藏7次。参考文档 https://blog.csdn.net/huangyabin001/article/details/28434989https://blog.csdn.net/huangyabin001/article/details/28435093#commentshttps://blog.csdn.net/jieqiong1/article/details/712629871..._android 输入法源码

Ubuntu下使用nnUNet 训练自己的数据集(只管实现,不讲原理,通俗易懂)_nnunet训练自己的数据集-程序员宅基地

本文介绍了如何在Ubuntu下使用nnUNet训练自己的数据集,不涉及原理解析,只提供实现步骤。包括nnUNet简介、修改训练参数和文件位置等操作。详细内容可参考Tina的博文。

SSM实现秒杀系统案例-程序员宅基地

文章浏览阅读9.9k次,点赞10次,收藏88次。对于抢购系统来说,首先要有可抢购的活动,而且这些活动具有促销性质,这种大型活动的负载可能是平时的几十倍,所以通过增加硬件、优化瓶颈代码等手段是很难达到目标的,所以抢购系统得专门设计。在这里我们说的库存不是真正意义上的库存,其实是该促销可以抢购的数量,真正的库存在基础库存服务。用户点击『提交订单』按钮后,在抢购系统中获取了资格后才去基础库存服务中扣减真正的库存;而抢购系统控制的就是资格/剩余数。传统方案利用数据库行锁,但是在促销高峰数据库压力过大导致服务不可用,目前采用redis集群(16分片)缓存促销信息,

随便推点

正则表达式匹配各种括号内内容_正则表达式 匹配多个括号-程序员宅基地

文章浏览阅读2.8w次,点赞10次,收藏9次。用正则表达式匹配两个字符中间的文本String skh ="(?<=\\《)[^\\》]+";//用于匹配《》里面的文本String str="但实际上《kajdwdej》孙大伟多";//测试字符串Pattern pattern=Pattern.compile(skh); Matcher matcher=pattern.matcher(str); boolean is=matche_正则表达式 匹配多个括号

Android 中的Shape_linear radial sweep分别代表的什么-程序员宅基地

文章浏览阅读553次。之前一直看项目用过这个东西,但是自己都不怎么熟悉,大概就知道可以画一些圆角之类的~ 今天就来好好了解一下吧~Shape里面有很多属性,依次学习一下第一步~首先来写一个Button这个布局文件就不贴了...太简单了~ (PS:说贴出来的站出来,我保证不打死你!)接下来开始学习第一个属性:Solid:(填充)在Drawable里面创建一个butt_linear radial sweep分别代表的什么

graphpad细胞增殖曲线_如何用GraphPad Prism绘制剂量反应曲线?-程序员宅基地

文章浏览阅读6.1k次。上图是Sci文献中的dose–response curves (剂量反应曲线),横坐标是药物GS-Se-SG浓度的对数值,纵坐标是Rrelative cell viability(相对细胞活性,% of control),从图注中可以知道IC50为5.1 μM。那么什么是IC50呢?IC50 (half maximal inhibitory concentration)是指被测量的拮抗剂的半抑制浓..._graphpad细胞增殖曲线

二叉查找树(非递归、递归遍历)_二叉排序树非递归查找-程序员宅基地

文章浏览阅读658次。二叉查找树(含递归、非递归遍历)_二叉排序树非递归查找

spark-sql 查询报错:Invalid method name: ‘get_table_req‘_invalid method name: 'get_table_req-程序员宅基地

文章浏览阅读3.1k次。spark-sql> select * from zps_d001 limit 1;Error in query: org.apache.hadoop.hive.ql.metadata.HiveException: Unable to fetch table zps_xxx. Invalid method name: 'get_table_req'org.apache.spark.sql.AnalysisException: org.apache.hadoop.hive.ql.metadata.H_invalid method name: 'get_table_req

unknown mutation type:-程序员宅基地

文章浏览阅读1.7w次。我这边使用得是vue+elementvue得状态组成actions 这个是异步请求 通过异步请求得话 然后在调用 mutations.jsmutations.js 将数据提交到这里 this.$commit(‘test’,testTemp)index.js 这个是将数据初始化getters 这个是将 this.$store.getters.test 就能获取到了 testTemp写法是这个写法最后得原因是因为我这在 index.js 和mutation.js 为定义常量##_unknown mutation type

推荐文章

热门文章

相关标签