Skip to content

Commit

Permalink
v1.0.4-alpha-3: 禁用用户以及删除用户时,同时删除其所有 AccessToken 和 RefreshToken。
Browse files Browse the repository at this point in the history
  • Loading branch information
Hansin1997 committed Mar 13, 2021
1 parent d4aada1 commit 4b6e2e0
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 6 deletions.
2 changes: 1 addition & 1 deletion auth-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>cn.dustlight.auth</groupId>
<artifactId>auth-parent</artifactId>
<version>1.0.4-alpha-2</version>
<version>1.0.4-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>auth-core</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion auth-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>cn.dustlight.auth</groupId>
<artifactId>auth-parent</artifactId>
<version>1.0.4-alpha-2</version>
<version>1.0.4-alpha-3</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>auth-service</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ public RedisTokenStore authTokenStore(@Autowired RedisConnectionFactory redisCon

@Bean("enhancedTokenStore")
@ConditionalOnMissingBean(name = "enhancedTokenStore")
public EnhancedRedisTokenStore enhancedRedisTokenStore(@Autowired RedisConnectionFactory redisConnectionFactory) {
return new EnhancedRedisTokenStore(redisConnectionFactory);
public EnhancedRedisTokenStore enhancedRedisTokenStore(@Autowired RedisConnectionFactory redisConnectionFactory,
@Autowired RedisTokenStore redisTokenStore) {
return new EnhancedRedisTokenStore(redisConnectionFactory, redisTokenStore);
}

@Bean("authApprovalStore")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import cn.dustlight.auth.entities.DefaultUser;
import cn.dustlight.auth.entities.DefaultUserRole;
import cn.dustlight.auth.entities.User;
import cn.dustlight.auth.services.oauth.EnhancedRedisTokenStore;
import cn.dustlight.auth.services.storages.StorageHandler;
import cn.dustlight.auth.services.UserService;
import cn.dustlight.auth.util.Constants;
Expand Down Expand Up @@ -53,6 +54,9 @@ public class UserResource {
@Autowired
protected StorageHandler storageHandler;

@Autowired
protected EnhancedRedisTokenStore enhancedRedisTokenStore;

/**
* 判断 Client 与用户是否拥有某权限。
*/
Expand Down Expand Up @@ -101,12 +105,20 @@ public User createUser(@RequestParam(name = "username") String username,
@DeleteMapping("users/{uid}")
@io.swagger.v3.oas.annotations.Operation(summary = "删除用户(永久删除)", description = "应用和用户需要 DELETE_USER 权限。")
public void deleteUser(@PathVariable Long uid) {
DefaultUser user = userService.loadUser(uid);
if (user == null)
ErrorEnum.USER_NOT_FOUND.throwException();
userService.deleteUsers(Arrays.asList(uid));
try {
storageHandler.remove(generateAvatarKey(uid));
} catch (Exception e) {
ErrorEnum.DELETE_USER_AVATAR_FAIL.details(e.getMessage());
}
try {
enhancedRedisTokenStore.deleteUserToken(user.getUsername());
} catch (IOException e) {
ErrorEnum.DELETE_USER_TOKEN_FAIL.throwException();
}
logger.debug(String.format("删除用户: [%s] ", uid));
}

Expand Down Expand Up @@ -170,6 +182,14 @@ public void updateUserExpiredAt(@PathVariable Long uid, @RequestParam Date expir
@Operation(summary = "设置用户封禁或解封", description = "封禁或解封用户。应用和用户需拥有 LOCK_USER 权限。")
public void updateUserEnabled(@PathVariable Long uid, @RequestParam Boolean enabled) {
userService.updateEnabled(Arrays.asList(uid), enabled);
if(!enabled){
DefaultUser user = userService.loadUser(uid);
try {
enhancedRedisTokenStore.deleteUserToken(user.getUsername());
} catch (IOException e) {
ErrorEnum.DELETE_USER_TOKEN_FAIL.throwException();
}
}
}

@PreAuthorize("(#oauth2.client or hasAnyAuthority('WRITE_USER_EMAIL')) and #oauth2.clientHasRole('WRITE_USER_EMAIL')")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
package cn.dustlight.auth.services.oauth;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;

import java.io.IOException;
import java.util.Set;

public class EnhancedRedisTokenStore {

private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";

private static final String USERNAME_TO_ACCESS = "uname_to_access:";

private RedisConnectionFactory redisConnectionFactory;
private RedisTokenStore redisTokenStore;
private String prefix = "";

private static final Log logger = LogFactory.getLog(EnhancedRedisTokenStore.class);

private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();

public EnhancedRedisTokenStore(RedisConnectionFactory connectionFactory) {
public EnhancedRedisTokenStore(RedisConnectionFactory connectionFactory, RedisTokenStore redisTokenStore) {
this.redisTokenStore = redisTokenStore;
this.redisConnectionFactory = connectionFactory;
}

Expand All @@ -29,6 +46,14 @@ protected byte[] serializeKey(String key) {
return serializationStrategy.serialize(prefix + key);
}

protected String deserializeKey(byte[] bytes) {
return serializationStrategy.deserializeString(bytes);
}

protected <T> T deserialize(byte[] bytes, Class<T> clazz) {
return serializationStrategy.deserialize(bytes, clazz);
}

protected RedisConnection getConnection() {
return this.redisConnectionFactory.getConnection();
}
Expand All @@ -38,4 +63,27 @@ public Long countClientToken(String clientId) {
return conn.setCommands().sCard(serializeKey(CLIENT_ID_TO_ACCESS + clientId));
}
}

public void deleteUserToken(String username) throws IOException {
try (RedisConnection conn = getConnection()) {
try (Cursor<byte[]> cursor = conn.keyCommands().scan(ScanOptions.scanOptions()
.match(USERNAME_TO_ACCESS + "*:" + username)
.count(Long.MAX_VALUE)
.build())) {
cursor.forEachRemaining(bytes -> {
String key = deserializeKey(bytes);
Set<byte[]> tokenSet = conn.sMembers(bytes);
if (tokenSet == null)
return;
tokenSet.forEach(tokenBytes -> {
DefaultOAuth2AccessToken accessToken = deserialize(tokenBytes, DefaultOAuth2AccessToken.class);
redisTokenStore.removeAccessToken(accessToken);
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
if (refreshToken != null)
redisTokenStore.removeRefreshToken(refreshToken);
});
});
}
}
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<groupId>cn.dustlight.auth</groupId>
<artifactId>auth-parent</artifactId>
<name>auth-parent</name>
<version>1.0.4-alpha-2</version>
<version>1.0.4-alpha-3</version>
<description>Parent project of OAuth2 Server based on Spring Cloud Security</description>

<modules>
Expand Down

0 comments on commit 4b6e2e0

Please sign in to comment.