io.irain.reactor.security.handler.TokenServerAuthenticationSuccessHandler Maven / Gradle / Ivy
package io.irain.reactor.security.handler;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import io.irain.reactor.commons.bean.R;
import io.irain.reactor.commons.bean.Result;
import io.irain.reactor.core.util.BeanUtils;
import io.irain.reactor.security.constants.TokenConstants;
import io.irain.reactor.security.domain.ClientInfo;
import io.irain.reactor.security.domain.CurrentUser;
import io.irain.reactor.security.domain.TokenInfo;
import io.irain.reactor.security.repository.AuthenticationRepository;
import io.irain.reactor.security.service.ClientInfoService;
import io.irain.reactor.security.util.ResponseUtils;
import io.vavr.Tuple;
import io.vavr.Tuple3;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import static io.irain.reactor.commons.enums.CommonExceptionEnum.LOGIN_TOKEN_CACHE_ERROR;
/**
* @author youta
**/
@Component
@RequiredArgsConstructor
public class TokenServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
private final AuthenticationRepository authenticationRepository;
private final ObjectProvider clientProvider;
@Override
public Mono onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
ClientInfoService clientInfoService = clientProvider.getIfAvailable(() -> new ClientInfoService() {
@Override
public Mono loadClientById(String clientId) {
return Mono.just(ClientInfo.base("test", "test"));
}
});
return clientInfoService.client(webFilterExchange.getExchange())
.map(clientInfo -> Tuple.of(IdUtil.fastSimpleUUID(), IdUtil.fastSimpleUUID(), clientInfo))
.flatMap(tuple -> getToken(authentication, tuple))
.flatMap(token -> ResponseUtils.build(webFilterExchange.getExchange().getResponse(), Result.ok(token)));
}
private Mono getToken(Authentication authentication, Tuple3 tokens) {
CurrentUser userDetails = (CurrentUser) authentication.getPrincipal();
if (CharSequenceUtil.isEmpty(userDetails.getUserId())) {
userDetails.setUserId(userDetails.getId());
}
ClientInfo clientInfo = tokens._3;
var tokenInfo = BeanUtils.copy(userDetails, TokenInfo.class);
tokenInfo
.setAccessToken(tokens._1)
.setRefreshToken(tokens._2)
.setExpiresIn(clientInfo.getExpire())
.setUserId(userDetails.getId())
.setRoles(userDetails.getRoleInfos())
.setAuthority(userDetails.getAuthorities()
.stream()
.map(SimpleGrantedAuthority::getAuthority)
.toList());
return this.authenticationRepository.getTokenList(TokenConstants.tokenList(tokenInfo.getUsername()))
.defaultIfEmpty(new ArrayList<>())
.flatMap(tokensList -> {
if (tokensList.size() >= clientInfo.getConcurrentLoginCount()) {
String firstToken = tokensList.get(0);
tokensList.remove(0);
tokensList.add(tokens._1);
return this.authenticationRepository.delete(TokenConstants.token(firstToken))
.thenReturn(tokensList);
}
tokensList.add(tokens._1);
return Mono.just(tokensList);
})
.flatMap(tokensList -> authenticationRepository.tokenList(TokenConstants.tokenList(tokenInfo.getUsername()), tokensList, clientInfo.getRefreshExpire())
.then(
authenticationRepository.token(TokenConstants.token(tokens._1), userDetails.getUsername(), clientInfo.getExpire())
.filter(Boolean.TRUE::equals)
.flatMap(x -> authenticationRepository.refreshToken(TokenConstants.refresh(tokens._2), userDetails.getUsername(), clientInfo.getRefreshExpire()))
.filter(Boolean.TRUE::equals)
.flatMap(x -> authenticationRepository.user(TokenConstants.session(tokens._1), userDetails, clientInfo.getRefreshExpire()))
.filter(Boolean.TRUE::equals)
.switchIfEmpty(Mono.defer(() -> R.error(LOGIN_TOKEN_CACHE_ERROR)))
.map(x -> tokenInfo)
));
}
}