Keycloak 入门实战(4)--客户端适配器使用

Keycloak 提供了客户端适配器来方便各应用程序的接入,本文主要介绍在 Tomcat 和 SpringBoot 应用中如何使用 Keycloak 来保护资源;文中使用到的软件版本:Java 1.8.0_191、Keycloak 1.16.1、Tomcat 8 .5.76、SpringBoot 2.5.9。

1、Keycloak 地址

这里假设 Keycloak 已安装完成,地址为 http://10.49.196.10:8080/auth。Keycloak 的安装方法可以参考:Keycloak 入门实战(2)--安装

2、Tomcat 中使用 Keycloak

2.1、下载 Tomcat 的适配器

https://www.keycloak.org/archive/downloads-16.1.1.html

2.2、安装适配器

把下载的文件(keycloak-oidc-tomcat-adapter-16.1.1.tar.gz)解压到 Tomcat 的 lib 目录下。

2.3、设置 Keycloak Valve

在 web 应用的 META-INF 目录下创建 context.xml 文件:

<Context>
    <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>

2.4、生成 keycloak.json 文件

假设自己 web 应用的 context-path 为 /keycloak-tomcat,在 Keycloak 中创建一个客户端:client-tomcat

在该客户端的安装 tab 页,格式选 “Keycloak OIDC JSON”,点击  “下载” 按钮。

 把下载下来的 keycloak.json 文件拷贝到 web 应用的 WEB-INF 目录下。

2.5、修改应用 web.xml 文件定义资源保护规则

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <security-constraint>
        <!--登出url不需要保护-->
        <web-resource-collection>
            <web-resource-name>logout</web-resource-name>
            <url-pattern>/logout.jsp</url-pattern>
        </web-resource-collection>
    </security-constraint>

    <security-constraint>
        <!--被保护的资源-->
        <web-resource-collection>
            <web-resource-name>admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
            <url-pattern>/system/*</url-pattern>
        </web-resource-collection>
        <!--可以访问保护资源的角色,这里出现的角色需要在 security-role 中声明-->
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <!--被保护的资源-->
        <web-resource-collection>
            <web-resource-name>user</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <!--可以访问保护资源的角色,这里出现的角色需要在 security-role 中声明-->
        <auth-constraint>
            <role-name>user</role-name>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <!--登录方法-->
        <auth-method>KEYCLOAK</auth-method>
        <!--领域名称,可为空-->
        <realm-name></realm-name>
    </login-config>

    <!--声明角色-->
    <security-role>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <role-name>user</role-name>
    </security-role>
</web-app>

2.6、创建用户及角色

在 Keycloak 中创建如下用户和角色:

用户 拥有角色
admin admin
test-user user

2.7、测试应用

把 web 应用部署到 Tomcat 中并访问应用:http://localhost:8080/keycloak-tomcat,此时页面会跳转到 Keycloak 的登录页面:

 输入用户名密码后会跳转到自己应用的页面。使用 admin 用户登录后,可以访问应用的所有 url;如果使用 test-user 用户登录并访问 http://localhost:8080/keycloak-tomcat/admin/index.jsp 会报 403 错误:

2.8、登出

这里为了方便使用一个 jsp(/logout.jsp) 来执行登出并跳到应用首页:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
  request.logout();
  response.sendRedirect(request.getContextPath());
%>

访问 http://localhost:8080/keycloak-tomcat/logout.jsp,由于登出后会跳到应用的首页,首页属于被保护的资源,所以又会跳转到 Keycloak 的登录页面。

Tomcat 整合 Keycloak 的详细说明可参考官网:https://www.keycloak.org/docs/16.1/securing_apps/index.html#_spring_boot_adapter。

3、SpringBoot 中使用 Keycloak

3.1、引入依赖

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.keycloak.bom</groupId>
            <artifactId>keycloak-adapter-bom</artifactId>
            <version>16.1.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    ...
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-boot-starter</artifactId>
    </dependency>
</dependencies>

3.2、创建客户端

假设 SpringBoot 应用的 context-path 为 /,在 Keycloak 中创建客户端:client-springboot

3.3、创建用户及角色

在 Keycloak 中创建如下用户和角色:

用户 拥有角色
admin admin
test-user user

3.4、定义资源保护规则(application.yml)

keycloak:
  public-client: true
  auth-server-url: http://10.49.196.10:8080/auth
  realm: master
  resource: client-springboot
  security-constraints:
    -  #登出 url 不需要权限控制
      securityCollections:
        - name: logout
          patterns:
            - /logout
    - #管理员相关资源保护, admin 角色的用户可以访问
      authRoles:
        - admin
      securityCollections:
        -
          name: admin
          patterns:
            - /admin/*
            - /system/*
    - #用户相关资源保护, user 或 admin 角色的用户可以访问
      authRoles:
        - admin
        - user
      securityCollections:
        -
          name: user
          patterns:
            - /*

3.5、编写测试 Controller

package com.abc.demo.keycloak.controller;

import org.keycloak.KeycloakPrincipal;
import org.keycloak.representations.AccessToken;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.util.Set;

@RequestMapping("/*")
@RestController
public class TestController {
    @ResponseBody
    @RequestMapping("/")
    public String welcome() {
        return "welcome";
    }

    @ResponseBody
    @RequestMapping("/admin/index")
    public String adminIndex() {
        return "admin index";
    }

    @ResponseBody
    @RequestMapping("/user/index")
    public String userIndex() {
        return "user index";
    }

    /**
     * 获取用户信息
     * @param principal
     * @return
     */
    @ResponseBody
    @RequestMapping("/getUserInfo")
    public String getUserInfo(Principal principal) {
        String result = "";
        if (principal instanceof KeycloakPrincipal) {
            AccessToken accessToken = ((KeycloakPrincipal) principal).getKeycloakSecurityContext().getToken();
            String preferredUsername = accessToken.getPreferredUsername();
            AccessToken.Access realmAccess = accessToken.getRealmAccess();
            Set<String> roles = realmAccess.getRoles();
            result = "当前登录用户:" + preferredUsername + ", 角色:" + roles;
        }
        return result;
    }

    /**
     * 登出
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("/logout")
    public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.logout();
        response.sendRedirect(request.getContextPath());
    }
}

3.6、测试应用

访问应用:http://localhost:8080,此时页面会跳转到 Keycloak 的登录页面:

 输入用户名密码后会跳转到自己应用的 url。使用 admin 用户登录后,可以访问应用的所有 url;如果使用 test-user 用户登录并访问 http://localhost:8080/admin/index 会报 403 错误:

3.7、登出

访问登出 url:http://localhost:8080/logout,由于登出后会跳到应用的首页,首页属于被保护的资源,所以又会跳转到 Keycloak 的登录页面。

SpringBoot 整合 Keycloak 的详细说明可参考官网:https://www.keycloak.org/docs/16.1/securing_apps/index.html#_spring_boot_adapter。

4、可能出现问题

4.1、Token is not active

在单点登录的时候应用可能会报如下错误:

org.keycloak.adapters.OAuthRequestAuthenticator.resolveCode failed verification of token: Token is not active

这是由于 Keycloak 运行的机器和应用运行的机器时间相差较大导致的,处理方法:修改时间错误的机器上时间,使各机器上的时间一致。

 

posted @ 2022-04-17 10:36  且行且码  阅读(1846)  评论(0编辑  收藏  举报