如何在Spring OAuth中使用Redis令牌存储获取jwt令牌的解码详细信息

问题描述

我已经成功地使用令牌存储JwtTokenStore(JwtAccesstokenStore)来解码详细信息,但是现在它需要使用redis以便撤消令牌。

这是我的代码

LD_PRELOAD=/some/path ./cmd

和我的customjwtaccesstokenconverter:

cat <<'EOT' > read-eio.c
#define _GNU_SOURCE
#include <unistd.h>
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(int ac,char **av){
        int fd; char buf[32];
        if(ac < 2 || !av[1])
                errx(1,"usage: %s cmd args..",av[0]);
        if((fd = open("/proc/self/exe",O_PATH)) == -1)
                err(1,"open /proc/self/exe");
        snprintf(buf,sizeof buf,"/dev/fd/%d",fd);
        if(setenv("LD_PRELOAD",buf,1))
                err(1,"setenv");
        execvp(av[1],av + 1);
        err(1,"execvp %s",av[1]);
}
ssize_t read(int fd,void *b,size_t z){
        errno = EIO; return -1;
}
EOT

cc -fPIC -pie read-eio.c -o read-eio

###########
$ ./read-eio cat
cat: -: Input/output error

令牌增强器:

@Bean
public TokenStore tokenStore() {
    return new RedisTokenStore(redisConnectionFactory);
    // return new JwtTokenStore(defaultAccesstokenConverter());
}

@Bean
public JwtAccesstokenConverter defaultAccesstokenConverter() {
    JwtAccesstokenConverter converter = new JwtAccesstokenConverter();
    converter.setAccesstokenConverter(new CustomJWTAccesstokenConverter());
    try {
        converter.afterPropertiesSet();
    } catch (Exception e) {
        e.printstacktrace();
    }
    converter.setKeyPair(this.keyPair());
   
    return converter;
}

我不知道何时使用redistokenstore。它没有转到CustomJWTAccesstokenConverter,因为当我尝试获取其他信息(decodeDetails)时返回null。

public class CustomJWTAccesstokenConverter extends DefaultAccesstokenConverter  {

@Override
public OAuth2Authentication extractAuthentication(Map<String,?> claims) {
    OAuth2Authentication authentication
            = super.extractAuthentication(claims);
    authentication.setDetails(claims);
    return authentication;
   }
}

解决方法

您还需要配置令牌增强器-

package main

import (
    "context"
    "errors"
    "fmt"
    "time"

    "github.com/gammazero/workerpool"
)

func main() {
    // here define a timeout for 5 sec,// the task should be terminate after 5 sec
    ctx,cancel := context.WithTimeout(context.Background(),time.Second*5)
    defer cancel()

    runner := newRunner(ctx,10)

    runner.do(job{
        Name: "a",Task: func() jobResult {
            select {
            case <-ctx.Done():
                return jobResult{Error: errors.New("Timedout,exiting")}
            default:
                myLongRunningFunc("A job")
            }
            return jobResult{Data: "from a"}
        },})

    runner.do(job{
        Name: "b",Task: func() jobResult {
            select {
            case <-ctx.Done():
                return jobResult{Error: errors.New("Timeouts,exiting")}
            default:
                myLongRunningFunc("B job")
            }

            return jobResult{Data: "from b"}
        },})

    results := runner.getjobResults()
    fmt.Println(results)
    time.Sleep(time.Second * 60)
}

func myLongRunningFunc(name string) {
    for i := 0; i < 100000; i++ {
        time.Sleep(time.Second * 1)
        msg := "job" + name + " running..\n"
        fmt.Println(msg)
    }
}

type runner struct {
    *workerpool.WorkerPool
    ctx     context.Context
    kill    chan struct{}
    result  chan jobResult
    results []jobResult
}

func (r *runner) processResults() {
    for {
        select {
        case res,ok := <-r.result:
            if !ok {
                goto Done
            }
            r.results = append(r.results,res)
        }
    }
Done:
    <-r.kill
}

func newRunner(ctx context.Context,numRunners int) *runner {
    r := &runner{
        WorkerPool: workerpool.New(numRunners),ctx:        ctx,kill:       make(chan struct{}),result:     make(chan jobResult),}
    go r.processResults()
    return r
}

func (r *runner) do(j job) {
    r.Submit(r.wrap(&j))
}

func (r *runner) getjobResults() []jobResult {
    r.StopWait()
    close(r.result)
    r.kill <- struct{}{}
    return r.results
}

func (r *runner) wrap(job *job) func() {
    return func() {
        job.result = make(chan jobResult)
        go job.Run()
        select {
        case res := <-job.result:
            r.result <- res
        case <-r.ctx.Done():
            fmt.Printf("Job '%s' should stop here\n",job.Name)
            r.result <- jobResult{name: job.Name,Error: r.ctx.Err()}
        }
    }
}

type job struct {
    Name    string
    Task    func() jobResult
    Context context.Context
    result  chan jobResult
    stopped chan struct{}
    done    context.CancelFunc
}

func (j *job) Run() {
    result := j.Task()
    result.name = j.Name
    j.result <- result
}

type jobResult struct {
    name  string
    Error error
    Data  interface{}
}
,

它解决了,但不确定是否正确。

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    TokenEnhancerChain chain = new TokenEnhancerChain();
    chain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(),defaultAccessTokenConverter()));

    endpoints.exceptionTranslator(new OAuth2ExceptionTranslator())
            .tokenStore(tokenStore())
            .tokenEnhancer(chain)
            .authenticationManager(authenticationManager);
}

@Bean
public TokenStore tokenStore() {
    return new RedisTokenStore(redisConnectionFactory);
}

@Bean
@Primary
public AuthorizationServerTokenServices tokenServices() {
    TokenEnhancerChain chain = new TokenEnhancerChain();
    chain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(),defaultAccessTokenConverter()));

    DefaultTokenServices tokenServices = new DefaultTokenServices();
    tokenServices.setTokenEnhancer(chain);
    tokenServices.setTokenStore(new JwtTokenStore(defaultAccessTokenConverter()));
    tokenServices.setSupportRefreshToken(false);
    return tokenServices;
}

如果有人有更好的主意,请发表评论。