3 回答
TA贡献2003条经验 获得超2个赞
是的,您可以使用 OAuth 2.0 客户端凭据流程和服务帐户。
Keycloak 建议使用 3 种方法来保护 SpringBoot REST 服务的安全:
带有 Keycloak Spring Boot 适配器
带 keycloak Spring 安全适配器
与 OAuth2 / OpenID 连接
请注意将您的客户端配置为:
访问类型:机密
授权:已启用
服务帐户(OAuth 客户端凭据流程):已启用
请注意将您的目标服务配置为:
访问类型:仅承载
因此,调用者应该是confidential
,目标服务应该是bearer-only
。
创建您的用户、角色、映射器...并将角色分配给您的用户。
检查您的 spring 项目中是否有此依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
配置要在 REST 客户端 (application.properties) 中使用的身份验证,例如:
security.oauth2.client.client-id=employee-service
security.oauth2.client.client-secret=68977d81-c59b-49aa-aada-58da9a43a850
security.oauth2.client.user-authorization-uri=${rest.security.issuer-uri}/protocol/openid-connect/auth
security.oauth2.client.access-token-uri=${rest.security.issuer-uri}/protocol/openid-connect/token
security.oauth2.client.scope=openid
security.oauth2.client.grant-type=client_credentials
JwtAccessTokenCustomizer像 Arun 的示例一样实现您的SecurityConfigurer (ResourceServerConfigurerAdapter) 。
最后实现你的服务控制器:
@RestController
@RequestMapping("/api/v1/employees")
public class EmployeeRestController {
@GetMapping(path = "/username")
@PreAuthorize("hasAnyAuthority('ROLE_USER')")
public ResponseEntity<String> getAuthorizedUserName() {
return ResponseEntity.ok(SecurityContextUtils.getUserName());
}
@GetMapping(path = "/roles")
@PreAuthorize("hasAnyAuthority('ROLE_USER')")
public ResponseEntity<Set<String>> getAuthorizedUserRoles() {
return ResponseEntity.ok(SecurityContextUtils.getUserRoles());
}
}
TA贡献1873条经验 获得超9个赞
按照@dmitri-algazin 实施工作流程,您基本上有两个选择:
如果您想涵盖除 Keycloak 之外的其他 IdM,它以某种方式解决了单一责任原则,我会使用RestTemplate. 您可以在下面找到变量:
//Constants
@Value("${keycloak.url}")
private String keycloakUrl;
@Value("${keycloak.realm}")
private String keycloakRealm;
@Value("${keycloak.client_id}")
private String keycloakClientId;
RestTemplate restTemplate = new RestTemplate();
private static final String BEARER = "BEARER ";
首先,您需要生成访问令牌:
@Override
public AccessTokenResponse login(KeycloakUser user) throws NotAuthorizedException {
try {
String uri = keycloakUrl + "/realms/" + keycloakRealm +
"/protocol/openid-connect/token";
String data = "grant_type=password&username="+
user.getUsername()+"&password="+user.getPassword()+"&client_id="+
keycloakClientId;
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/x-www-form-urlencoded");
HttpEntity<String> entity = new HttpEntity<String>(data, headers);
ResponseEntity<AccessTokenResponse> response = restTemplate.exchange(uri,
HttpMethod.POST, entity, AccessTokenResponse.class);
if (response.getStatusCode().value() != HttpStatus.SC_OK) {
log.error("Unauthorised access to protected resource", response.getStatusCode().value());
throw new NotAuthorizedException("Unauthorised access to protected resource");
}
return response.getBody();
} catch (Exception ex) {
log.error("Unauthorised access to protected resource", ex);
throw new NotAuthorizedException("Unauthorised access to protected resource");
}
}
然后使用令牌,您可以从用户那里检索信息:
@Override
public String user(String authToken) throws NotAuthorizedException {
if (! authToken.toUpperCase().startsWith(BEARER)) {
throw new NotAuthorizedException("Invalid OAuth Header. Missing Bearer prefix");
}
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authToken);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<AccessToken> response = restTemplate.exchange(
keycloakUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/userinfo",
HttpMethod.POST,
entity,
AccessToken.class);
if (response.getStatusCode().value() != HttpStatus.SC_OK) {
log.error("OAuth2 Authentication failure. "
+ "Invalid OAuth Token supplied in Authorization Header on Request. Code {}", response.getStatusCode().value());
throw new NotAuthorizedException("OAuth2 Authentication failure. "
+ "Invalid OAuth Token supplied in Authorization Header on Request.");
}
log.debug("User info: {}", response.getBody().getPreferredUsername());
return response.getBody().getPreferredUsername();
}
您可以将此 URL 替换为 @dimitri-algazin 提供的 URL,以检索所有用户信息。
可以使用 Keycloak 依赖项:
<!-- keycloak -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>3.4.3.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.1.4.Final</version>
</dependency>
并使用类来生成令牌:
Keycloak keycloak = KeycloakBuilder
.builder()
.serverUrl(keycloakUrl)
.realm(keycloakRealm)
.username(user.getUsername())
.password(user.getPassword())
.clientId(keycloakClientId)
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build())
.build();
return keycloak.tokenManager().getAccessToken();
示例摘自此处。我们还将镜像上传到 Docker Hub,以方便与 Keycloak 交互。因此我们从选项 2) 开始。目前,我们正在覆盖其他 IdM,我们选择了选项 1),以避免包含额外的依赖项。结论:
如果您坚持使用 Keycloak,我会选择选项 2 ,因为类包含 Keycloak 工具的额外功能。我会选择选项 1以获得更多的覆盖范围和其他 OAuth 2.0 工具。
TA贡献1833条经验 获得超4个赞
我们有类似的需求,通过用户 uuid 获取用户电子邮件。
创建服务用户,确保用户具有“领域管理”->“查看用户”角色(也可能是查询用户)
过程很简单:使用服务用户登录 keycloak(将密码和/或用户名编码在属性文件中),使用授权标头中的 accessToken 向 keycloak 发出请求
获取 http://{yourdomainadress}/auth/admin/realms/{yourrealmname}/users/{userId}
使用REST API登录keycloak的方法:
POST http://{yourdomainadress}/auth/realms/{yourrealmname}/protocol/openid-connect/token
标题:
内容类型:application/x-www-form-urlencoded
正文 x-www-form-urlencoded:
client_id:您的客户
用户名:您正在使用的用户
密码:用户密码
grant_type:密码
client_secret:11112222-3333-4444-5555-666666666666(如果客户端“访问类型”=“机密”,则需要客户端密钥)
很快:确保您的服务用户分配了正确的角色来执行操作、登录、查询 keycloak(检查文档以获取正确的查询 url 和参数,总是具有挑战性)
添加回答
举报