package com.youlai.auth.authentication.wechat; import cn.hutool.core.util.StrUtil; import com.youlai.auth.authentication.password.ResourceOwnerPasswordAuthenticationToken; import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * 参数解析 * * @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter */ public class WechatAuthenticationConverter implements AuthenticationConverter { public static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; @Override public Authentication convert(HttpServletRequest request) { // grant_type (REQUIRED) String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); if (!"wechat".equals(grantType)) { return null; } MultiValueMap parameters = getParameters(request); // scope (OPTIONAL) String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { throwError( OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE, ACCESS_TOKEN_REQUEST_ERROR_URI); } Set requestedScopes = null; if (StringUtils.hasText(scope)) { requestedScopes = new HashSet<>( Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); } // code (REQUIRED) String code = parameters.getFirst("code"); if (StrUtil.isBlank(code)) { throwError( OAuth2ErrorCodes.INVALID_REQUEST, "code", ACCESS_TOKEN_REQUEST_ERROR_URI); } // encryptedData (REQUIRED) String encryptedData = parameters.getFirst("encryptedData"); if (StrUtil.isBlank(encryptedData)) { throwError( OAuth2ErrorCodes.INVALID_REQUEST, "encryptedData", ACCESS_TOKEN_REQUEST_ERROR_URI); } // iv (REQUIRED) String iv = parameters.getFirst("iv"); if (StrUtil.isBlank(iv)) { throwError( OAuth2ErrorCodes.INVALID_REQUEST, "iv", ACCESS_TOKEN_REQUEST_ERROR_URI); } Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); if (clientPrincipal == null) { throwError( OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.INVALID_CLIENT, ACCESS_TOKEN_REQUEST_ERROR_URI); } Map additionalParameters = parameters .entrySet() .stream() .filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) && !e.getKey().equals(OAuth2ParameterNames.SCOPE)) .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0))); ResourceOwnerPasswordAuthenticationToken resourceOwnerPasswordAuthenticationToken = new ResourceOwnerPasswordAuthenticationToken( clientPrincipal, requestedScopes, additionalParameters ); return resourceOwnerPasswordAuthenticationToken; } public static MultiValueMap getParameters(HttpServletRequest request) { Map parameterMap = request.getParameterMap(); MultiValueMap parameters = new LinkedMultiValueMap(parameterMap.size()); parameterMap.forEach((key, values) -> { for (String value : values) { parameters.add(key, value); } }); return parameters; } public static void throwError(String errorCode, String parameterName, String errorUri) { OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri); throw new OAuth2AuthenticationException(error); } }