Spring Security Oauth2:RedisTokenStore(一)之Token内容揭秘

[本文仅仅作为自我学习的笔记,有什么不对或误人之处,望大佬指正]

RedisTokenStore之Token内容揭秘

Token存储方式

Spring Security Oauth2 存储Token的方式有多种, 比如JWT、Jdbc(数据库)、Redis等,根据Oauth2继承类图,实现方式如下:
TokenStrore实现方式
使用Redis存储Token具有明显的优势,我自己开发学习的过程使用RedisTokenStore。

Redis key存储内容

在使用Redis存储token,spring security oauth2 会生成以下几个key, 直接放出RedisTokenStore的源码吧:

	private static final String ACCESS = "access:";
	private static final String AUTH_TO_ACCESS = "auth_to_access:";
	private static final String AUTH = "auth:";
	private static final String REFRESH_AUTH = "refresh_auth:";
	private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
	private static final String REFRESH = "refresh:";
	private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
	private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
	private static final String UNAME_TO_ACCESS = "uname_to_access:";

这几个存储的key都存了什么内容,有什么含义呢?
于是我打开redis-cli,迫不及待的看一下这个key “shield:oauth:access:x8U6xmAK0MeFDEJ0”(“shield:oauth:”是项目自定义的前缀)shield:oauth:access:x8U6xmAK0MeFDEJ0
尼玛,这是什么东西?我们来看一下RedisTokenStore的源码:

//spring secuity oauth2提供的一个序列化工具
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
//存储OAuth2AccessToken
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
		byte[] serializedAccessToken = serialize(token);
		......
}
//把存储对象序列化
private byte[] serialize(Object object) {
	return serializationStrategy.serialize(object);
}

通过源码,我们发现spring security oauth2存储的是序列化后的对象,而不是json。(注意:应该是为提高存储效率,而不是加密操作)
那我们就以access:[AccessToken]这个key为例,在自己项目里写一个controller看看这个对象内容(自己debug也行,junit单元测试也行,自己能看到内容就好)

    @ApiOperation(value = "获取access存储内容", httpMethod = "GET")
    @GetMapping("/deserialize/access")
    public R<Object> deserializeAccessToken(
        @ApiParam("accessToken") @NotBlank(message = "accessToken不能为空") @RequestParam("accessToken") String accessToken
    ) {
        RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
        byte[] serializedKey = serializationStrategy.serialize(Prefix.REDIS_SHIELD_OAUTH2 + "access:" + accessToken);
        RedisConnection conn = connectionFactory.getConnection();
        byte[] bytes;
        try {
            bytes = conn.get(serializedKey);
        } finally {
            conn.close();
        }
        OAuth2AccessToken content = serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
        return RUtil.success(content);
    }

经过这个controller,还原成我们常用的json格式,如下:

{
    "access_token": "x8U6xmAK0MeFDEJ0",
    "token_type": "bearer",
    "refresh_token": "0qLDRZE70MeFDEI!",
    "expires_in": 29658,
    "scope": "server"
  }

好了,既然我们key的内容知道了,我们就逐一分析一下这几个key的用处。

1. access:[AccessToken](对应对象:OAuth2AccessToken)

要如何获取这个key? 如果你已有spring security oauth2的项目(如果没有,可以github或spring官网找一个sample项目或自己搭建一个),可以类似这样发送一个请求:

http://localhost:6799/oauth/token?grant_type=password&username=wuji&password=12345678&client_id=app&client_secret=app

spring security oauth2获取AccessToken如果是密码授权方式(grant_type=password)除了携带用户名以及密码还要携带client_id和client_secret,当然,你也可以Http Basic方式将client_id和client_secret到请求头,如下:HTTP Basic
YXBwOmFwcA==是“client_id:client_secret” base64编码后的结果。
好了,我们试着发送一个请求,返回结果如下:

{
	//token,拿着这个token我们就可以资源(接口)了
    "access_token": "x8U6xmAK0MeFDEJ0",
    //token类型是一个票据类型,还有授权码类型等等
    "token_type": "bearer",
    //用与刷新access_token(资源访问token)的token
    "refresh_token": "0qLDRZE70MeFDEI!",
    //access_token剩余存活时间(单位是秒)
    "expires_in": 29658,
    //拿这个token可以访问那些范围内的资源
    "scope": "server"
  }
2. auth_to_access:[这里不再是AccessToken]

通过阅读源码,发现:

public String extractKey(OAuth2Authentication authentication) {
	//省略一些代码
	values.put(USERNAME, authentication.getName());
	values.put(CLIENT_ID, authorizationRequest.getClientId());
	values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
	return generateKey(values);
}

auth_to_access:key, key是将username、client_id、scope三个值加密后的值,我们再看auth_to_access:存储的内容如下:

{
    "access_token": "x8U6xmAK0MeFDEJ0",
    "token_type": "bearer",
    "refresh_token": "0qLDRZE70MeFDEI!",
    "expires_in": 24126,
    "scope": "server"
  }

跟access:的内容一模一样,那我们就知道了,代码内部实现可以通过username、client_id、scope 3个字段获取AccessToken。

3.auth:[accessToken] (对应对象:OAuth2Authentication)

auth:存的内容如下:

{
    "authorities": [{
            "authority": "ROLE_USER"
        },
        {
            "authority": "USER_RETRIEVE"
        }
    ],
    "details": null,
    "authenticated": true,
    "userAuthentication": {
        "authorities": [{
                "authority": "ROLE_USER"
            },
            {
                "authority": "USER_RETRIEVE"
            }
        ],
        "details": {
            "client_secret": "app",
            "grant_type": "password",
            "client_id": "app",
            "username": "wuji"
        },
        "authenticated": true,
        "principal": {
            "password": null,
            "username": "wuji",
            "authorities": [{
                    "authority": "ROLE_USER"
                },
                {
                    "authority": "USER_RETRIEVE"
                }
            ],
            "accountNonExpired": true,
            "accountNonLocked": true,
            "credentialsNonExpired": true,
            "enabled": true
        },
        "credentials": null,
        "name": "wuji"
    },
    "credentials": "",
    "principal": {
        "password": null,
        "username": "wuji",
        "authorities": [{
                "authority": "ROLE_USER"
            },
            {
                "authority": "USER_RETRIEVE"
            }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "oauth2Request": {
        "clientId": "app",
        "scope": ["server"],
        "requestParameters": {
            "grant_type": "password",
            "client_id": "app",
            "username": "wuji"
        },
        "resourceIds": [],
        "authorities": [],
        "approved": true,
        "refresh": false,
        "redirectUri": null,
        "responseTypes": [],
        "extensions": {},
        "grantType": "password",
        "refreshTokenRequest": null
    },
    "clientOnly": false,
    "name": "wuji"
}

主要包含当前登录用户的信息,以及用户附带的角色和和权限信息、生成Token时的授权方式等信息。

4.access_to_refresh:[accessToken]

access_to_refresh:存储的内容很简单,在通过password等授权方式获取token时的refreshToken

//refresh_token
grz0Xlzi0MeQwkx9
5.refresh_to_access:[refreshToken]

拿refreshToken去刷新accessToken时,会将新生成的accessToken放到refresh_to_access:

//access_token
TesxUOBt0MeRDDxA
6.refresh:[refreshToken]

存储内容如下:

{
 //refresh_toekn
  "value": "grz0Xlzi0MeQwkx9",
  //过期时间戳(mills)
  "expiration": 1557821765322
}

拿refreshToken去刷新accessToken时, 会先拿到这个KEY的信息,判断请求方的refresh token是否有效,无效的不能刷新access token。

7.refresh_auth:[refreshToken] (对应对象:OAuth2Authentication)

refresh_auth:存的内容与auth:类似,如下:

{
  "authorities": [
    {
      "authority": "ROLE_USER"
    },
    {
      "authority": "USER_RETRIEVE"
    }
  ],
  "details": null,
  "authenticated": true,
  "userAuthentication": {
    "authorities": [
      {
        "authority": "ROLE_USER"
      },
      {
        "authority": "USER_RETRIEVE"
      }
    ],
    "details": {
      "client_secret": "app",
      "grant_type": "password",
      "client_id": "app",
      "username": "wuji"
    },
    "authenticated": true,
    "principal": {
      "password": null,
      "username": "wuji",
      "authorities": [
        {
          "authority": "ROLE_USER"
        },
        {
          "authority": "USER_RETRIEVE"
        }
      ],
      "accountNonExpired": true,
      "accountNonLocked": true,
      "credentialsNonExpired": true,
      "enabled": true
    },
    "credentials": null,
    "name": "wuji"
  },
  "credentials": "",
  "principal": {
    "password": null,
    "username": "wuji",
    "authorities": [
      {
        "authority": "ROLE_USER"
      },
      {
        "authority": "USER_RETRIEVE"
      }
    ],
    "accountNonExpired": true,
    "accountNonLocked": true,
    "credentialsNonExpired": true,
    "enabled": true
  },
  "oauth2Request": {
    "clientId": "app",
    "scope": ["server"],
    "requestParameters": {
      "grant_type": "password",
      "client_id": "app",
      "username": "wuji"
    },
    "resourceIds": [],
    "authorities": [],
    "approved": true,
    "refresh": false,
    "redirectUri": null,
    "responseTypes": [],
    "extensions": {},
    "grantType": "password",
    "refreshTokenRequest": null
  },
  "clientOnly": false,
  "name": "wuji"
}
8.client_id_to_access:[client_id]

存储内容:

[{
    "access_token": "TesxUOBt0MeRDDxA",
    "token_type": "bearer",
    "refresh_token": "grz0Xlzi0MeQwkx9",
    "expires_in": 41714,
    "scope": "server"
}]

顾名思义,这个key将对应client_id的AccessToken对象存储了起来,因为不同的username、scope使用同一个client_id去请求获取token,所以这是一个list。

9.uname_to_access:[client_id:username]

存储内容:

[{
    "access_token": "TesxUOBt0MeRDDxA",
    "token_type": "bearer",
    "refresh_token": "grz0Xlzi0MeQwkx9",
    "expires_in": 41714,
    "scope": "server"
}]

这里也是一个list。

相关文章
  • 14
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Security OAuth2 Authorization Server可以通过Token增强器(Token Enhancer)来为授权后返回的Access Token添加额外的信息。Token增强器是一个接口,它接收一个Access Token并返回一个增强后的Access Token。在Authorization Server配置类中,可以通过调用tokenEnhancer()方法来设置Token增强器,示例代码如下: ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private DataSource dataSource; @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123456"); return converter; } @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(tokenStore()) .accessTokenConverter(accessTokenConverter()) .tokenEnhancer(tokenEnhancer()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .allowFormAuthenticationForClients(); } } ``` 在上面的代码中,CustomTokenEnhancer是一个自定义的Token增强器,它可以在Access Token中添加额外的信息。示例代码如下: ```java public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("organization", authentication.getName() + "@test.com"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } } ``` 在上面的代码中,我们向Access Token中添加了一个名为“organization”的信息,它的值为当前用户的用户名加上@test.com。这种方式可以为Access Token添加任何我们需要的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值