gpt4 book ai didi

java - CAS 自定义身份验证处理程序主体 JSON 问题

转载 作者:搜寻专家 更新时间:2023-11-01 03:30:47 27 4
gpt4 key购买 nike

在我的自定义身份验证处理程序的重写的 authenticateUsernamePasswordInternal 函数中返回自定义属性时,我遇到了 JSON 问题:

return createHandlerResult( credential,
this.getPrincipalFactory( ).createPrincipal( credential.getId( ), attributes) );

哪个 createPrincipal 方法接受 Map<String, Object>
Principal createPrincipal(String id, Map<String, Object> attributes);

当我放 Map<String, List>在属性中,CAS 返回列表的 toString 表示而不是其 JSON 表示。简而言之,如何从此函数返回正确的 JSON 属性序列化?

笔记:
  • 使用的 CAS 版本:5.3.8
  • 通过 AbstractUsernamePasswordAuthenticationHandler 扩展的自定义身份验证
  • 实现了使用 CAS 协议(protocol)的 JWT

  • 这是我到目前为止尝试过的:

    1) CAS 在验证服务时将 HashMap 的 List 转换为 String(可能是根本原因)

    当我将 Principal 创建为 Map<String, new ArrayList<new HashMap<>> ,我的 HashMap 被转换为 HashMap 的 toString 表示。所以它的类型信息现在从 HashMap -> String 转换,这使得 CAS 向我的客户端返回不正确的 JSON,因为 String 像 JSON 一样被序列化。这里发生了 ->
    AbstractUrlBasedTicketValidator -> validate() -> final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);

    这里 serverResponse 包含:
    <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
    <cas:authenticationSuccess>
    <cas:user>test</cas:user>
    <cas:attributes>
    <cas:roles>(Test,[ADMIN])</cas:roles>
    </cas:attributes>
    </cas:authenticationSuccess>
    </cas:serviceResponse>

    我的期望:
    <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
    <cas:authenticationSuccess>
    <cas:user>test</cas:user>
    <cas:attributes>
    <cas:roles>
    <cas:Test>ADMIN</cas:Test>
    </cas:roles>
    </cas:attributes>
    </cas:authenticationSuccess>
    </cas:serviceResponse>

    2) 为 Map<String, Object> 在对象中返回带有虚拟映射的新主体

    当我将 HashMap 添加到 Map<String, Object> 的对象部分时,它返回给客户端 {"left": "key", "right": "value"}["key":"value"] map 。
    我调试了这么久,我看到CAS在请求/tickets URL时如何使用json-smart-2.3库。我看到当我在 Map 的 Object 中为属性发送 Map 时,json-smart 库使用其 BeansWriter 来序列化 Map,它获取类的字段并用作键。因此,我发送 HashMap -> CAS 将其转换为 Java Pair(在下一步中描述)-> Pair 具有“左”和“右”属性,因此它将左右字段添加到我不想要的 JSON 正文中。

    3) 调试 CAS 以了解它如何将属性序列化为 JSON 以进行 API 调用( token url)
  • 我寻找 CAS 以了解它如何处理 Principal,它将所有内容合并为 Pair 的 LinkedList。因此,无论我在 Map 对象部分添加什么,它都会以 JSON 表示形式返回一个数组,如 []。这意味着当我添加 attributes.put("name", "ExampleName") ,它返回为 "{"name":["ExampleName"]}因为CAS调用mergeAttributes DefaultAuthenticationResultBuilder 的功能类,Principal 中的所有内容在我们在 Principal 对象创建中发送的函数中作为 List 返回 Map<String, Object> .所以这意味着每个属性都作为列表返回?还下载了 CAS 源代码,并看到他们的测试断言类似于 principal.getAttributes()[0] 这暗示这是默认行为?我在任何地方都看不到任何文档,但没有意义。

  • 4) 在 Map<String, Object> 的对象中返回具有 JSON 表示的新主体(几乎是一个解决方案)

    我还尝试在属性的 Object 部分直接返回 JSON 表示:
    Map<String, Object> attributes = new HashMap<>();
    String roles = "{"TestModule":["Name1"]}"; (didn't add escape quotes for simplicity)
    attributes.put("roles", roles);

    对于对 的 API 调用,它按预期返回 JSON。/ticket URL 因为序列化库尝试序列化我发送的字符串所以这是一种令人困惑的解决方案,但仍然存在一些问题。如果我通过 登录/登录 页面,CAS 再次用 [] 包装每个属性。当我调试时,我看到这一次 CAS 没有使用它在我调用时使用的序列化程序 /ticket 网址。当 CAS 开始使用 cas-server-core-webflow-api 时,我试图调试更多但卡在某个地方

    我不想要这个:
    {"rolesPerModule":["{\"TestModuleForBouncer_LIVE\":[\"ADMIN\"]}"]}

    或这个:
    {"name":[ExampleName]} *(yes, no "" returned here)*

    我想要:
    {"rolesPerModule":{"{\"TestModuleForBouncer_LIVE\":[\"ADMIN\"]}"}}

    或这个
    {"name":"ExampleName"}

    最佳答案

    最后,我找到了根本原因。如果您在这里寻找您的 Principal 属性具有 {"left": "key", "right": "value"} instead of["key":"value"] 的原因在这里,我将首先尝试显示根本原因和我的解决方案:

    为什么对/v1/tickets 的请求的响应 JSON 中有“左”和“右”属性?

    1) 你返回 new SimplePrincipal(id, new HashMap)

    2) CAS 将所有属性合并到一个集合中。你可以找到它:

    DefaultAuthenticationResultBuilder -> mergeAttributes()

    然后它调用
    CollectionUtils.toCollection(entry.getValue(), ArrayList.class)

    3) 在函数内部查看这些行:
    else if (obj instanceof Collection) {
    c.addAll((Collection<Object>) obj);
    LOGGER.trace("Converting multi-valued attribute [{}]", obj);
    } else if (obj instanceof Map) {
    final Set<Map.Entry> set = ((Map) obj).entrySet();
    c.addAll(set.stream().map(e -> Pair.of(e.getKey(), e.getValue())).collect(Collectors.toSet()));
    }

    如果您的属性是 Map,则它们的值将流式传输为 .因此,您的 hashmaps 值类型更改为 现在。

    4) 然后 CAS 开始创建您的 JSON。看着 JWTTokenTicketBuilder -> buildJwt函数(它正在由另一个类处理,即 CAS 6.X 版本中的 JwtBuilder,但问题仍然相同)

    5) CAS 使用 nimbus-jose-jwt (v5.10) 创建 JWTClaims。

    6) nimbus-jose-jwt 使用 json-smart (v2.3) 返回 JWTObject。

    7) CAS 调用 object.toJSONString()(JWTObject 的函数)将您的属性序列化为 JSON。这是它发生的部分,但它也与我之前详细编写的步骤有关。

    8) json-smart 库不处理 Pair 类型,它使用默认编写器来处理它们不处理的类型,这是 BeansWriterASM 的情况。该编写器获取类的所有属性并将它们用作 JSON 的键和它们的值。

    9) 所以在这种情况下你的值(value) "name":"test" -> 变成 "left":"name", "right":"test" CAS 在第 3 步配对。由于 json-smart 不处理 Pair 类,它返回这个 JSON。

    是的,说来话长,但我想清楚地分享我的经历。 json-smart 库已经很久没有更新了,nimbus-jose-jwt 库计划在他们的下一个版本中更改 json-smart 库( https://bitbucket.org/connect2id/nimbus-jose-jwt/pull-requests/50/wip-allow-replacing-json-smart-with/diff ),然后 CAS 也可能会更改它,但对于两者来说似乎很长的路要走.

    变通方法/解决方案

    1) 不要在 SimplePrincipal 中返回 Map 的实例。相反,在属性的根上使用集合。因为就像 步骤 3 上面,如果您的值是 Collection 的实例,CAS 不会用 Pair 包装您的值。例如,我的工作示例是:
        final Map<String, Object> test= new HashMap<>( );
    test.put( "faultyJSON", yourAttributes); // don't do this
    test.put( "properJSON", Collections.singleton( yourAttributes ) ); // make this

    return createHandlerResult( credential,
    this.getPrincipalFactory( ).createPrincipal( credential.getId( ), test) );

    这将使您的 JSON 在根目录上具有无意义的数组,但如前所述,这是目前的解决方法。

    2) 使用 JSONAware 类包装您的属性,该 json-smart 库允许您返回自己的 JSONString 表示。这不是安全的解决方案,因为如果您更改 CAS 版本并且如果 CAS 更改了任何库实现,那么此解决方案可能会让您头疼,但无论如何我也会为此分享我的工作示例:
    public class JsonWrapper<T> implements JSONAware, Serializable
    {
    @JsonValue
    public T attributes;

    public JsonWrapper( T attributes )
    {
    this.attributes = attributes;
    }

    @Override public String toJSONString( )
    {
    String json = "{}";
    try
    {
    json = new ObjectMapper( ).writeValueAsString( attributes );
    }
    catch ( JsonProcessingException e )
    {
    LoggerFactory.getLogger( getClass( ) )
    .error( "Couldn't map attributes: {}. Returning default: {}", attributes, json );
    }
    return json;
    }
    }

    当 json-smart 的序列化开始时,此类将返回其自己的 JSON 表示。您还需要使用此类包装所有属性,例如:
    yourAttributes.forEach( ( k, v ) -> yourAttributes.put( k, new JsonWrapper<> (v) ) )
    return createHandlerResult( credential,
    this.getPrincipalFactory( ).createPrincipal( credential.getId( ), yourAttributes) );

    3) 你可以像 JsonPairWriter 一样实现自己的 Writer 并将其注册到 JsonWriter 的 writerList 中。我试过这个,它也有效,但与上述相比,它可能是最愚蠢的解决方案,因为有很多维护和错误的副作用,请记住。

    最后但同样重要的是,当您调用 时不会发生这种情况。/登录 CAS 的端点,这意味着通过浏览器获取 token 。据我所知,它有不同的工作流程来返回属性和 json,而不是我上面描述的流程。不确定,但服务和所有属性等信息是通过 REST 调用获取的并获得一些 XML 响应,因此将其解析给客户端。

    关于java - CAS 自定义身份验证处理程序主体 JSON 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57788482/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com