技术标签: 开发实例
具体开发参考:https://blog.csdn.net/m0_46267097/article/details/106092404
注意:依赖包的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>
需要去掉上面引用的几个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
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());
}
}
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);
}
}
使用上述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-10.0.1\bin\standalone.bat启动项目,登录进入Admin Console
左侧菜单栏选择User Federation后点击右侧下拉框,可以看见我们加入的Provider出现了。
选择后进入其配置页面
通过这种方法获得的用户角色名为offline_access,有了角色名,也有了数据来源,后期我们的用户验证就可以开始为所欲为喽。
具体开发参考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开发文档
目录QT参数文件运行指令文本qt读取命令行界面完整程序QT参数文件qt寻找参数文件//头文件:#include<QFileDialog>#include<QSettings>//寻找参数文件QSettings *IniFile = new QSettings("./Config/Config.ini", QSettings::IniFormat);//参数文件路径 ui->lineEdit->setText(IniFile->value( _qt 读取命令行
HTML5中<template>标签的详细介绍(图文)这篇文章主要介绍了HTML5中的template标签,是HTML5入门中的重要知识,需要的朋友可以参考一、HTML5 template元素初面<template>元素,基本上可以确定是2013年才出现的。干嘛用的呢,顾名思意,就是用来声明是“模板元素”。目前,我们在HTML中嵌入模板HT..._标签是默认隐藏的吗?
首先,必须先说明使用Audio Queue来同时进行录音和播放的优势。如果想在iOS设备上播放和录制音频,苹果推荐我们使用AVFoundation框架中的AVAudioPlayer和AVAudioRecorder类。虽然用法比较简单,但是不支持流式;这就意味着:在播放音频前,必须等到整个音频加载完成后,才能开始播放音频;录音时,也必须等到录音结束后,才能获取到录音数据。这给应用造成了很大的局限性。_avaudiorecorder 与audioqueueref能同时录音吗
extends LineEditfunc _input(event): if event is InputEventKey: #仅允许汉字输入 if !(event.unicode >= 0x4E00 && event.unicode <= 0x9FA5): get_tree().set_input_as_handled()常用Unicode编码范围汉字 [0x4e00,0x9fa5]数字 [0x30,0x39]小写字母 [0x61,0x7a]
java技术3大特性:1、JVM --- java虚拟机的基本功能如下:(1) 通过classloader寻找和裝载class文件(2)解释字节码成指令并执行,提供class文件运行环境(3)进行运行期间垃圾回收(4)提供与硬件交互的平台编译器编译java源代码后,会生成字节码,这是为JVM生成的机器码指令。2、垃圾回收利用系统级线程跟踪内存分配
C++ 实现一个虚拟聊天软件本文使用c++和Qt实现一个虚拟的IM聊天软件,实现消息传递,大文件传输,断点续传等功能,主要功能点包括:用户列表上下线更新列表消息喘息*文件传输(支持2GB以上大文件)多文件同时传输断点续传UDP发送/接收消息项目中所有关于消息的接收与发送均使用UDP进行,包括上线,下线消息,聊天消息,请求发送文件消息,接受方返回确认发送消息,取..._c加加实现im
文章目录1. CSV 导入的文件格式数据类型实体csv文件关系csv文件2. CSV 导入 neo4j 案例不同导入工具的区别:1. CSV 导入的文件格式官方DOC:https://neo4j.com/docs/operations-manual/current/tools/neo4j-admin/neo4j-admin-import/#import-tool-header-format数据类型int, long, float, double, boolean, byte, short, _头实体 尾实体 关系csv是什么
在Linux安装MySQL 5.5服务的时候,报错The server quit without updating PID file (/[失败]b/mysql/localhost.localdom...由此,我从另一个角度解决了问题——宝塔面板。_宝塔 error! the server quit without updating pid file (/www/server/data/63e1
引言在用C++的项目源码中,经常会不可避免的会看到下面的代码:?123456789#ifdef __cplusplusextern"C" { #endif /*...*/ #ifdef __cplusplus} #endif它到底
一、二叉树的遍历概念二叉树的遍历是指从根结点触发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。(1). 前(先)序遍历前序遍历:若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子书。特点:①. 根----->左------->右 ②. 根据前序遍历的结果可知第一个访问的必定是root结点。(2). 中序遍历中序遍历:若二叉树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然
如果只是使用百度SDK的单一功能,可以单独下载,但如果是结合功能如,定位和导航,百度建议我们去下载相关功能的组合包链接:http://lbsyun.baidu.com/sdk/download,在这里你选中你需要的功能下载,下载后的lib下有我们选择的全部lib,不需要单独去添加,避免了库之间的冲突;有一点要注意就是组合包与单独下载的库最好不要在同一个工程中使用,否则有可能引起库冲突。
VIM命令(全面详解)_linux vim