第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View File

@ -0,0 +1,138 @@
package com.fastbee.oauth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import javax.annotation.Resource;
import javax.sql.DataSource;
/**
* 授权服务器配置配置客户端id密钥和令牌的过期时间
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Resource
private DataSource dataSource;
@Resource
private AuthenticationManager authenticationManager;
@Resource
private UserDetailsService userDetailsService;
/**
* 用来配置令牌端点(Token Endpoint)的安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients()
.authenticationEntryPoint(new OAuth2AuthenticationEntryPoint());
// security.allowFormAuthenticationForClients()
// .tokenKeyAccess("permitAll()")
// // 允许 /oauth/token_check端点的访问
// .checkTokenAccess("permitAll()")
// .passwordEncoder(new PasswordEncoder() {
// @Override
// public String encode(CharSequence charSequence) {
// // 密码加密
// return null;
// }
//
// @Override
// public boolean matches(CharSequence charSequence, String s) {
// // 密码校验
// // return false;
// return true;
// }
// })
// .allowFormAuthenticationForClients();
}
/**
* 用来配置客户端详情服务
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(getClientDetailsService());
}
/**
* 用来配置授权authorization以及令牌token的访问端点和令牌服务(token services)。
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 查询用户、授权、分组,可以被重写
endpoints.userDetailsService(userDetailsService)
// 审批客户端的授权
.userApprovalHandler(userApprovalHandler())
// 授权审批
.approvalStore(approvalStore())
// 获取授权码
.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
// 验证token
.authenticationManager(authenticationManager)
// 查询、保存、刷新token
.tokenStore(this.getJdbcTokenStore());
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
@Bean
public UserApprovalHandler userApprovalHandler() {
return new SpeakerApprovalHandler(getClientDetailsService(), approvalStore(), oAuth2RequestFactory());
}
@Bean
public JdbcClientDetailsService getClientDetailsService() {
JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
jdbcClientDetailsService.setPasswordEncoder(passwordEncoder());
return jdbcClientDetailsService;
}
@Bean
public OAuth2RequestFactory oAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(getClientDetailsService());
}
@Bean
public TokenStore getJdbcTokenStore(){
TokenStore tokenStore = new JdbcTokenStore(dataSource);
return tokenStore;
}
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,48 @@
package com.fastbee.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import javax.sql.DataSource;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
TokenStore tokenStore = jdbcTokenStore();
OAuth2AuthenticationManager auth2AuthenticationManager= new OAuth2AuthenticationManager();
resources.authenticationManager(auth2AuthenticationManager);
resources.resourceId("speaker-service").tokenStore(tokenStore).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// 限制资源服务器只接管匹配的资源
http.requestMatchers().antMatchers("dueros")
.and()
//授权的请求
.authorizeRequests()
.anyRequest().authenticated()
//关闭跨站请求防护
.and()
.csrf().disable();
}
public TokenStore jdbcTokenStore(){
TokenStore tokenStore = new JdbcTokenStore(dataSource);
return tokenStore;
}
}

View File

@ -0,0 +1,83 @@
package com.fastbee.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import java.util.*;
/**
* kerwincui
*/
public class SpeakerApprovalHandler extends ApprovalStoreUserApprovalHandler {
private int approvalExpirySeconds = -1;
@Autowired
private ApprovalStore approvalStore;
public SpeakerApprovalHandler(JdbcClientDetailsService clientDetailsService, ApprovalStore approvalStore, OAuth2RequestFactory oAuth2RequestFactory) {
this.setApprovalStore(approvalStore);
this.setClientDetailsService(clientDetailsService);
this.setRequestFactory(oAuth2RequestFactory);
}
@Override
public AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
// 获取授权过的范围
Set<String> requestedScopes = authorizationRequest.getScope();
Set<String> approvedScopes = new HashSet<String>();
Set<Approval> approvals = new HashSet<Approval>();
Date expiry = computeExpiry();
// 存储授权或拒绝的范围
Map<String, String> approvalParameters = authorizationRequest.getApprovalParameters();
for (String requestedScope : requestedScopes) {
String approvalParameter = OAuth2Utils.SCOPE_PREFIX + requestedScope;
String value = approvalParameters.get(approvalParameter);
value = value == null ? "" : value.toLowerCase();
if ("true".equals(value) || value.startsWith("approve")||value.equals("on")) {
approvedScopes.add(requestedScope);
approvals.add(new Approval(userAuthentication.getName(), authorizationRequest.getClientId(),
requestedScope, expiry, Approval.ApprovalStatus.APPROVED));
}
else {
approvals.add(new Approval(userAuthentication.getName(), authorizationRequest.getClientId(),
requestedScope, expiry, Approval.ApprovalStatus.DENIED));
}
}
approvalStore.addApprovals(approvals);
boolean approved;
authorizationRequest.setScope(approvedScopes);
if (approvedScopes.isEmpty() && !requestedScopes.isEmpty()) {
approved = false;
}
else {
approved = true;
}
authorizationRequest.setApproved(approved);
return authorizationRequest;
}
private Date computeExpiry() {
Calendar expiresAt = Calendar.getInstance();
// 默认一个月
if (approvalExpirySeconds == -1) {
expiresAt.add(Calendar.MONTH, 1);
}
else {
expiresAt.add(Calendar.SECOND, approvalExpirySeconds);
}
return expiresAt.getTime();
}
}

View File

@ -0,0 +1,49 @@
package com.fastbee.oauth.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* kerwincui
*/
@Controller
@SessionAttributes("authorizationRequest")
public class ConfirmAccessController {
@Autowired
private JdbcClientDetailsService clientDetailsService;
@Autowired
private ApprovalStore approvalStore;
@RequestMapping("/oauth/confirm_access")
public String getAccessConfirmation(Map<String, Object> model, Principal principal ) {
AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest");
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
Map<String, String> scopes = new LinkedHashMap<String, String>();
for (String scope : clientAuth.getScope()) {
scopes.put(OAuth2Utils.SCOPE_PREFIX + scope, "false");
}
for (Approval approval : approvalStore.getApprovals(principal.getName(), client.getClientId())) {
if (clientAuth.getScope().contains(approval.getScope())) {
scopes.put(OAuth2Utils.SCOPE_PREFIX + approval.getScope(),
approval.getStatus() == Approval.ApprovalStatus.APPROVED ? "true" : "false");
}
}
model.put("auth_request", clientAuth);
model.put("client", client);
model.put("scopes", scopes);
return "oauth/access_confirmation";
}
}

View File

@ -0,0 +1,51 @@
package com.fastbee.oauth.controller;
import com.fastbee.framework.web.service.SysLoginService;
import com.fastbee.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class LoginController {
@Autowired
private TokenStore tokenStore;
@Autowired
private SysLoginService loginService;
@Autowired
private TokenService tokenService;
@RequestMapping("/oauth/login")
public String login() {
return "oauth/login";
}
@RequestMapping("/oauth/index")
public String index() {
return "oauth/index";
}
@GetMapping("/oauth/logout")
@ResponseBody
public String logout(@RequestHeader String Authorization) {
if (!Authorization.isEmpty()){
String token=Authorization.split(" ")[1];
OAuth2AccessToken auth2AccessToken = tokenStore.readAccessToken(token);
tokenStore.removeAccessToken(auth2AccessToken);
return "SUCCESS";
}else{
return "FAIL";
}
}
}

View File

@ -0,0 +1,107 @@
package com.fastbee.oauth.controller;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.controller.BaseController;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.page.TableDataInfo;
import com.fastbee.common.enums.BusinessType;
import com.fastbee.common.utils.poi.ExcelUtil;
import com.fastbee.oauth.domain.OauthClientDetails;
import com.fastbee.oauth.service.IOauthClientDetailsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 云云对接Controller
*
* @author kerwincui
* @date 2022-02-07
*/
@Api(tags = "云云对接")
@RestController
@RequestMapping("/iot/clientDetails")
public class OauthClientDetailsController extends BaseController
{
@Autowired
private IOauthClientDetailsService oauthClientDetailsService;
/**
* 查询云云对接列表
*/
@ApiOperation("查询云云对接列表")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:list')")
@GetMapping("/list")
public TableDataInfo list(OauthClientDetails oauthClientDetails)
{
startPage();
List<OauthClientDetails> list = oauthClientDetailsService.selectOauthClientDetailsList(oauthClientDetails);
return getDataTable(list);
}
/**
* 导出云云对接列表
*/
@ApiOperation("导出云云对接列表")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:export')")
@Log(title = "云云对接", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, OauthClientDetails oauthClientDetails)
{
List<OauthClientDetails> list = oauthClientDetailsService.selectOauthClientDetailsList(oauthClientDetails);
ExcelUtil<OauthClientDetails> util = new ExcelUtil<OauthClientDetails>(OauthClientDetails.class);
util.exportExcel(response, list, "云云对接数据");
}
/**
* 获取云云对接详细信息
*/
@ApiOperation("获取云云对接详细信息")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return AjaxResult.success(oauthClientDetailsService.selectOauthClientDetailsById(id));
}
/**
* 新增云云对接
*/
@ApiOperation("新增云云对接")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:add')")
@Log(title = "云云对接", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody OauthClientDetails oauthClientDetails)
{
return oauthClientDetailsService.insertOauthClientDetails(oauthClientDetails);
}
/**
* 修改云云对接
*/
@ApiOperation("修改云云对接")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:edit')")
@Log(title = "云云对接", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody OauthClientDetails oauthClientDetails)
{
return oauthClientDetailsService.updateOauthClientDetails(oauthClientDetails);
}
/**
* 修改云云对接
*/
@ApiOperation("删除云云对接")
@PreAuthorize("@ss.hasPermi('iot:clientDetails:remove')")
@Log(title = "云云对接", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(oauthClientDetailsService.deleteOauthClientDetailsByIds(ids));
}
}

View File

@ -0,0 +1,255 @@
package com.fastbee.oauth.controller;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.text.KeyValue;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.json.JsonUtils;
import com.fastbee.oauth.domain.OauthAccessToken;
import com.fastbee.oauth.domain.OauthApprovals;
import com.fastbee.oauth.domain.OauthClientDetails;
import com.fastbee.oauth.domain.OauthCode;
import com.fastbee.oauth.enums.OAuth2GrantTypeEnum;
import com.fastbee.oauth.service.IOauthApprovalsService;
import com.fastbee.oauth.service.IOauthClientDetailsService;
import com.fastbee.oauth.service.IOauthCodeService;
import com.fastbee.oauth.service.OauthAccessTokenService;
import com.fastbee.oauth.utils.HttpUtils;
import com.fastbee.oauth.utils.OAuth2Utils;
import com.fastbee.oauth.vo.OAuth2OpenAccessTokenRespVO;
import com.fastbee.oauth.vo.OAuth2OpenAuthorizeInfoRespVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.fastbee.common.core.domain.AjaxResult.success;
import static com.fastbee.common.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static com.fastbee.common.exception.ServiceExceptionUtil.exception0;
import static com.fastbee.common.utils.SecurityUtils.getUserId;
import static com.fastbee.common.utils.collection.CollectionUtils.convertList;
/**
* @author fastb
* @version 1.0
* @description: OAuth2.0 授权接口
* @date 2024-03-20 11:29
*/
@RestController
@RequestMapping("/oauth2")
@Slf4j
public class OauthController {
@Resource
private IOauthClientDetailsService oauthClientDetailsService;
@Resource
private IOauthApprovalsService oAuthApproveService;
@Resource
private OauthAccessTokenService oauthAccessTokenService;
@Resource
private IOauthCodeService oauthCodeService;
@GetMapping("/authorize")
public AjaxResult authorize(@RequestParam("clientId") String clientId) {
// 0. 校验用户已经登录。通过 Spring Security 实现
// 1. 获得 Client 客户端的信息
OauthClientDetails oauthClientDetails = oauthClientDetailsService.validOAuthClientFromCache(clientId);
// 2. 获得用户已经授权的信息
List<OauthApprovals> approves = oAuthApproveService.getApproveList(getUserId(), clientId);
// 拼接返回
return success(this.convert(oauthClientDetails, approves));
}
private OAuth2OpenAuthorizeInfoRespVO convert(OauthClientDetails oauthClientDetails, List<OauthApprovals> approves) {
// 构建 scopes
List<String> strings = StringUtils.str2List(oauthClientDetails.getScope(), ",", true, true);
List<KeyValue<String, Boolean>> scopes = new ArrayList<>(strings.size());
Map<String, OauthApprovals> approveMap = approves.stream().collect(Collectors.toMap(OauthApprovals::getScope, Function.identity()));
for (String scope : strings) {
OauthApprovals oauthApprovals = approveMap.get(scope);
scopes.add(new KeyValue<>(scope, oauthApprovals != null ? "true".equals(oauthApprovals.getStatus()) : false));
}
// 拼接返回
return new OAuth2OpenAuthorizeInfoRespVO(
new OAuth2OpenAuthorizeInfoRespVO.Client(oauthClientDetails.getClientId(), oauthClientDetails.getIcon()), scopes);
}
@PostMapping("/authorize")
public AjaxResult authorize(@RequestParam("response_type") String responseType,
@RequestParam("client_id") String clientId,
@RequestParam(value = "scope", required = false) String scope,
@RequestParam("redirect_uri") String redirectUri,
@RequestParam(value = "auto_approve") Boolean autoApprove,
@RequestParam(value = "state", required = false) String state) throws IOException {
log.warn("oauth2.0认证");
Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class);
scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap());
// 0. 校验用户已经登录。通过 Spring Security 实现
// 1.1 校验 responseType 是否满足 code 或者 token 值
OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType);
// 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内
OauthClientDetails client = oauthClientDetailsService.validOAuthClientFromCache(clientId, null,
grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri);
// 2.1 假设 approved 为 null说明是场景一
if (Boolean.TRUE.equals(autoApprove)) {
// 如果无法自动授权通过,则返回空 url前端不进行跳转
if (!oAuthApproveService.checkForPreApproval(getUserId(), clientId, scopes.keySet())) {
return success(null);
}
} else { // 2.2 假设 approved 非 null说明是场景二
// 如果计算后不通过,则跳转一个错误链接
if (!oAuthApproveService.updateAfterApproval(getUserId(), clientId, scopes)) {
return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state,
"access_denied", MessageUtils.message("user.access.denied")));
}
}
// 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向
List<String> approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) {
String redirect = getAuthorizationCodeRedirect(getUserId(), client, approveScopes, redirectUri, state);
return success(MessageUtils.message("authorization.success"), redirect);
}
return success();
// 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向
// return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state));
}
private String getAuthorizationCodeRedirect(Long userId, OauthClientDetails client,
List<String> scopes, String redirectUri, String state) {
// 1. 创建 code 授权码
String authorizationCode = generateCode();
OauthCode oauthCode = new OauthCode();
oauthCode.setCode(authorizationCode);
oauthCode.setUserId(userId);
oauthCodeService.insertOauthCode(oauthCode);
// String authorizationCode = oauthCodeService.grantAuthorizationCodeForCode(userId, client.getClientId(), scopes,
// redirectUri, state);
// 2. 拼接重定向的 URL
return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
}
private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
if (StrUtil.equals(responseType, "code")) {
return OAuth2GrantTypeEnum.AUTHORIZATION_CODE;
}
if (StrUtil.equalsAny(responseType, "token")) {
return OAuth2GrantTypeEnum.IMPLICIT;
}
throw exception0(BAD_REQUEST.getCode(), MessageUtils.message("oauth.response.type.not.valid"));
}
@PostMapping("/token")
public ResponseEntity<OAuth2OpenAccessTokenRespVO> postAccessToken(HttpServletRequest request,
@RequestParam("grant_type") String grantType,
@RequestParam(value = "code", required = false) String code, // 授权码模式
@RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式
@RequestParam(value = "state", required = false) String state, // 授权码模式
@RequestParam(value = "username", required = false) String username, // 密码模式
@RequestParam(value = "password", required = false) String password, // 密码模式
@RequestParam(value = "scope", required = false) String scope, // 密码模式
@RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式
// log.error("小度请求token,入参:{},{},{},{},{},{},{},{}", grantType, code, redirectUri, state, username, password, scope, refreshToken);
List<String> scopes = OAuth2Utils.buildScopes(scope);
// todo 小度传过来的参数重复了,这里先暂时处理一下
if (grantType.contains(",")) {
grantType = grantType.substring(grantType.indexOf(",") + 1);
}
if (code.contains(",")) {
code = code.substring(code.indexOf(",") + 1);
}
if (redirectUri.contains(",")) {
redirectUri = redirectUri.substring(redirectUri.indexOf(",") + 1);
}
// 1.1 校验授权类型
OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType);
if (grantTypeEnum == null) {
throw new ServiceException(MessageUtils.message("oauth.grant.type.null") + ":" + grantType + ";" + code + ";" + redirectUri);
}
if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) {
throw new ServiceException(MessageUtils.message("oauth.grant.type.implicit.not.support"));
}
// 1.2 校验客户端
String[] clientIdAndSecret = obtainBasicAuthorization(request);
OauthClientDetails client = oauthClientDetailsService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1],
grantType, scopes, redirectUri);
// 2. 根据授权模式,获取访问令牌
OauthAccessToken oauthAccessToken;
switch (grantTypeEnum) {
case AUTHORIZATION_CODE:
oauthAccessToken = oauthAccessTokenService.grantAuthorizationCodeForAccessToken(client, code, redirectUri, state);
break;
// case PASSWORD:
// accessTokenDO = oauth2GrantService.grantPassword(username, password, client.getClientId(), scopes);
// break;
// case CLIENT_CREDENTIALS:
// accessTokenDO = oauth2GrantService.grantClientCredentials(client.getClientId(), scopes);
// break;
// case REFRESH_TOKEN:
// accessTokenDO = oauth2GrantService.grantRefreshToken(refreshToken, client.getClientId());
// break;
default:
throw new IllegalArgumentException(MessageUtils.message("oauth.grant.type.null")+ "" + grantType);
}
Assert.notNull(oauthAccessToken, MessageUtils.message("oauth.access.token.null")); // 防御性检查
OAuth2OpenAccessTokenRespVO oAuth2OpenAccessTokenRespVO = this.convertAccessToken(oauthAccessToken);
ResponseEntity<OAuth2OpenAccessTokenRespVO> response = getResponse(oAuth2OpenAccessTokenRespVO);
// log.error("小度请求token成功{}", JSON.toJSONString(response));
return response;
}
private ResponseEntity<OAuth2OpenAccessTokenRespVO> getResponse(OAuth2OpenAccessTokenRespVO accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.set("Cache-Control", "no-store");
headers.set("Pragma", "no-cache");
headers.set("Content-Type", "application/json;charset=UTF-8");
return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
}
private OAuth2OpenAccessTokenRespVO convertAccessToken(OauthAccessToken oauthAccessToken) {
OAuth2OpenAccessTokenRespVO respVO = new OAuth2OpenAccessTokenRespVO();
respVO.setAccessToken(oauthAccessToken.getTokenId());
respVO.setRefreshToken(oauthAccessToken.getRefreshToken());
respVO.setTokenType("bearer");
respVO.setExpiresIn(OAuth2Utils.getExpiresIn(oauthAccessToken.getExpiresTime()));
// respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes()));
return respVO;
}
private String[] obtainBasicAuthorization(HttpServletRequest request) {
String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request);
if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) {
throw exception0(BAD_REQUEST.getCode(), MessageUtils.message("obtain.basic.authorization.failed"));
}
return clientIdAndSecret;
}
private static String generateCode() {
return IdUtil.fastSimpleUUID();
}
}

View File

@ -0,0 +1,39 @@
package com.fastbee.oauth.domain;
import lombok.*;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* @author fastb
* @date 2023-09-01 17:00
*/
@Data
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class OauthAccessToken {
private String tokenId;
private String token;
private String authenticationId;
private String userName;
private String clientId;
private String authentication;
private String refreshToken;
private String openId;
private Long userId;
private LocalDateTime expiresTime;
}

View File

@ -0,0 +1,42 @@
package com.fastbee.oauth.domain;
import com.fastbee.common.annotation.Excel;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 【请填写功能名称】对象 oauth_approvals
*
* @author kerwincui
* @date 2024-03-20
*/
@Data
public class OauthApprovals
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String userid;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String clientid;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String scope;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String status;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private LocalDateTime expiresat;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private LocalDateTime lastmodifiedat;
}

View File

@ -0,0 +1,285 @@
package com.fastbee.oauth.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 云云对接对象 oauth_client_details
*
* @author kerwincui
* @date 2022-02-07
*/
@ApiModel(value = "OauthClientDetails", description = "云云对接对象 oauth_client_details")
public class OauthClientDetails extends BaseEntity
{
private static final long serialVersionUID = 1L;
/**
* 主键编号
*/
@ApiModelProperty("编号")
@Excel(name = "编号")
private Long id;
/** 客户端ID */
@ApiModelProperty("客户端ID")
@Excel(name = "客户端ID")
private String clientId;
/** 资源 */
@ApiModelProperty("资源")
@Excel(name = "资源")
private String resourceIds;
/** 客户端秘钥 */
@ApiModelProperty("客户端秘钥")
private String clientSecret;
/** 权限范围 */
@ApiModelProperty("权限范围")
@Excel(name = "权限范围")
private String scope;
/** 授权模式 */
@ApiModelProperty("授权模式")
@Excel(name = "授权模式")
private String authorizedGrantTypes;
/** 回调地址 */
@ApiModelProperty("回调地址")
@Excel(name = "回调地址")
private String webServerRedirectUri;
/** 权限 */
@ApiModelProperty("权限")
@Excel(name = "权限")
private String authorities;
/** access token有效时间 */
@ApiModelProperty("access token有效时间")
@Excel(name = "access token有效时间")
private Long accessTokenValidity;
/** refresh token有效时间 */
@ApiModelProperty("refresh token有效时间")
@Excel(name = "refresh token有效时间")
private Long refreshTokenValidity;
/** 预留的字段 */
@ApiModelProperty("预留的字段")
@Excel(name = "预留的字段")
private String additionalInformation;
/** 自动授权 */
@ApiModelProperty("自动授权")
@Excel(name = "自动授权")
private String autoapprove;
/** 平台 */
@ApiModelProperty("平台")
@Excel(name = "平台")
private Integer type;
/**
* 启用状态
*/
@ApiModelProperty("启用状态")
@Excel(name = "启用状态")
private Integer status;
/**
* 图标
*/
@ApiModelProperty("图标")
private String icon;
/**
* 云技能id
*/
private String cloudSkillId;
/** 租户id */
private Long tenantId;
/** 租户名称 */
private String tenantName;
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
public String getTenantName() {
return tenantName;
}
public void setTenantName(String tenantName) {
this.tenantName = tenantName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCloudSkillId() {
return cloudSkillId;
}
public void setCloudSkillId(String cloudSkillId) {
this.cloudSkillId = cloudSkillId;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public void setClientId(String clientId)
{
this.clientId = clientId;
}
public String getClientId()
{
return clientId;
}
public void setResourceIds(String resourceIds)
{
this.resourceIds = resourceIds;
}
public String getResourceIds()
{
return resourceIds;
}
public void setClientSecret(String clientSecret)
{
this.clientSecret = clientSecret;
}
public String getClientSecret()
{
return clientSecret;
}
public void setScope(String scope)
{
this.scope = scope;
}
public String getScope()
{
return scope;
}
public void setAuthorizedGrantTypes(String authorizedGrantTypes)
{
this.authorizedGrantTypes = authorizedGrantTypes;
}
public String getAuthorizedGrantTypes()
{
return authorizedGrantTypes;
}
public void setWebServerRedirectUri(String webServerRedirectUri)
{
this.webServerRedirectUri = webServerRedirectUri;
}
public String getWebServerRedirectUri()
{
return webServerRedirectUri;
}
public void setAuthorities(String authorities)
{
this.authorities = authorities;
}
public String getAuthorities()
{
return authorities;
}
public void setAccessTokenValidity(Long accessTokenValidity)
{
this.accessTokenValidity = accessTokenValidity;
}
public Long getAccessTokenValidity()
{
return accessTokenValidity;
}
public void setRefreshTokenValidity(Long refreshTokenValidity)
{
this.refreshTokenValidity = refreshTokenValidity;
}
public Long getRefreshTokenValidity()
{
return refreshTokenValidity;
}
public void setAdditionalInformation(String additionalInformation)
{
this.additionalInformation = additionalInformation;
}
public String getAdditionalInformation()
{
return additionalInformation;
}
public void setAutoapprove(String autoapprove)
{
this.autoapprove = autoapprove;
}
public String getAutoapprove()
{
return autoapprove;
}
public void setType(Integer type)
{
this.type = type;
}
public Integer getType()
{
return type;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("clientId", getClientId())
.append("resourceIds", getResourceIds())
.append("clientSecret", getClientSecret())
.append("scope", getScope())
.append("authorizedGrantTypes", getAuthorizedGrantTypes())
.append("webServerRedirectUri", getWebServerRedirectUri())
.append("authorities", getAuthorities())
.append("accessTokenValidity", getAccessTokenValidity())
.append("refreshTokenValidity", getRefreshTokenValidity())
.append("additionalInformation", getAdditionalInformation())
.append("autoapprove", getAutoapprove())
.append("type", getType())
.toString();
}
}

View File

@ -0,0 +1,66 @@
package com.fastbee.oauth.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 【请填写功能名称】对象 oauth_code
*
* @author kerwincui
* @date 2024-03-20
*/
public class OauthCode extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String code;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private String authentication;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private Long userId;
public void setCode(String code)
{
this.code = code;
}
public String getCode()
{
return code;
}
public void setAuthentication(String authentication)
{
this.authentication = authentication;
}
public String getAuthentication()
{
return authentication;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("code", getCode())
.append("authentication", getAuthentication())
.append("userId", getUserId())
.toString();
}
}

View File

@ -0,0 +1,29 @@
package com.fastbee.oauth.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* OAuth2 授权类型(模式)的枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum OAuth2GrantTypeEnum {
PASSWORD("password"), // 密码模式
AUTHORIZATION_CODE("authorization_code"), // 授权码模式
IMPLICIT("implicit"), // 简化模式
CLIENT_CREDENTIALS("client_credentials"), // 客户端模式
REFRESH_TOKEN("refresh_token"), // 刷新模式
;
private final String grantType;
public static OAuth2GrantTypeEnum getByGranType(String grantType) {
return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values());
}
}

View File

@ -0,0 +1,22 @@
package com.fastbee.oauth.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fastbee.oauth.domain.OauthAccessToken;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface OauthAccessTokenMapper extends BaseMapper<OauthAccessToken> {
String selectUserNameByTokenId(String tokenId);
OauthAccessToken selectByTokenId(String tokenId);
void updateOpenIdByTokenId(@Param("tokenId") String tokenId,@Param("openUid") String openUid);
OauthAccessToken selectByUserName(String userName);
void insertOauthAccessToken(OauthAccessToken oauthAccessToken);
void deleteByUserId(Long userId);
}

View File

@ -0,0 +1,67 @@
package com.fastbee.oauth.mapper;
import com.fastbee.oauth.domain.OauthApprovals;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 【请填写功能名称】Mapper接口
*
* @author kerwincui
* @date 2024-03-20
*/
public interface OauthApprovalsMapper
{
/**
* 查询【请填写功能名称】
*
* @param userid 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
public OauthApprovals selectOauthApprovalsByUserid(String userid);
/**
* 查询【请填写功能名称】列表
*
* @param oauthApprovals 【请填写功能名称】
* @return 【请填写功能名称】集合
*/
public List<OauthApprovals> selectOauthApprovalsList(OauthApprovals oauthApprovals);
/**
* 新增【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
public int insertOauthApprovals(OauthApprovals oauthApprovals);
/**
* 修改【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
public int updateOauthApprovals(OauthApprovals oauthApprovals);
/**
* 删除【请填写功能名称】
*
* @param userid 【请填写功能名称】主键
* @return 结果
*/
public int deleteOauthApprovalsByUserid(String userid);
/**
* 批量删除【请填写功能名称】
*
* @param userids 需要删除的数据主键集合
* @return 结果
*/
public int deleteOauthApprovalsByUserids(String[] userids);
int update(OauthApprovals oauthApprovals);
List<OauthApprovals> selectListByUserIdAndClientId(@Param("userId") Long userId, @Param("clientId") String clientId);
}

View File

@ -0,0 +1,74 @@
package com.fastbee.oauth.mapper;
import com.fastbee.oauth.domain.OauthClientDetails;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 云云对接Mapper接口
*
* @author kerwincui
* @date 2022-02-07
*/
@Repository
public interface OauthClientDetailsMapper
{
/**
* 查询云云对接
*
* @param id 云云对接主键
* @return 云云对接
*/
public OauthClientDetails selectOauthClientDetailsById(Long id);
/**
* 查询云云对接列表
*
* @param oauthClientDetails 云云对接
* @return 云云对接集合
*/
public List<OauthClientDetails> selectOauthClientDetailsList(OauthClientDetails oauthClientDetails);
/**
* 新增云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
public int insertOauthClientDetails(OauthClientDetails oauthClientDetails);
/**
* 修改云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
public int updateOauthClientDetails(OauthClientDetails oauthClientDetails);
/**
* 删除云云对接
*
* @param clientId 云云对接主键
* @return 结果
*/
public int deleteOauthClientDetailsByClientId(String clientId);
/**
* 批量删除云云对接
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteOauthClientDetailsByIds(Long[] ids);
/**
* 通过授权平台查询配置
* @param type 授权平台类型
* @return
*/
OauthClientDetails selectOauthClientDetailsByType(@Param("type") Integer type, @Param("tenantId") Long tenantId);
OauthClientDetails selectOauthClientDetailsByClientId(String clientId);
}

View File

@ -0,0 +1,62 @@
package com.fastbee.oauth.mapper;
import com.fastbee.oauth.domain.OauthCode;
import java.util.List;
/**
* 【请填写功能名称】Mapper接口
*
* @author kerwincui
* @date 2024-03-20
*/
public interface OauthCodeMapper
{
/**
* 查询【请填写功能名称】
*
* @param code 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
public OauthCode selectOauthCodeByCode(String code);
/**
* 查询【请填写功能名称】列表
*
* @param oauthCode 【请填写功能名称】
* @return 【请填写功能名称】集合
*/
public List<OauthCode> selectOauthCodeList(OauthCode oauthCode);
/**
* 新增【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
public int insertOauthCode(OauthCode oauthCode);
/**
* 修改【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
public int updateOauthCode(OauthCode oauthCode);
/**
* 删除【请填写功能名称】
*
* @param code 【请填写功能名称】主键
* @return 结果
*/
public int deleteOauthCodeByCode(String code);
/**
* 批量删除【请填写功能名称】
*
* @param codes 需要删除的数据主键集合
* @return 结果
*/
public int deleteOauthCodeByCodes(String[] codes);
}

View File

@ -0,0 +1,70 @@
package com.fastbee.oauth.service;
import com.fastbee.oauth.domain.OauthApprovals;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 【请填写功能名称】Service接口
*
* @author kerwincui
* @date 2024-03-20
*/
public interface IOauthApprovalsService
{
/**
* 查询【请填写功能名称】
*
* @param userid 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
public OauthApprovals selectOauthApprovalsByUserid(String userid);
/**
* 查询【请填写功能名称】列表
*
* @param oauthApprovals 【请填写功能名称】
* @return 【请填写功能名称】集合
*/
public List<OauthApprovals> selectOauthApprovalsList(OauthApprovals oauthApprovals);
/**
* 新增【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
public int insertOauthApprovals(OauthApprovals oauthApprovals);
/**
* 修改【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
public int updateOauthApprovals(OauthApprovals oauthApprovals);
/**
* 批量删除【请填写功能名称】
*
* @param userids 需要删除的【请填写功能名称】主键集合
* @return 结果
*/
public int deleteOauthApprovalsByUserids(String[] userids);
/**
* 删除【请填写功能名称】信息
*
* @param userid 【请填写功能名称】主键
* @return 结果
*/
public int deleteOauthApprovalsByUserid(String userid);
boolean checkForPreApproval(Long userId, String clientId, Set<String> requestedScopes);
boolean updateAfterApproval(Long userId, String clientId, Map<String, Boolean> scopes);
List<OauthApprovals> getApproveList(Long userId, String clientId);
}

View File

@ -0,0 +1,70 @@
package com.fastbee.oauth.service;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.oauth.domain.OauthClientDetails;
import java.util.Collection;
import java.util.List;
/**
* 云云对接Service接口
*
* @author kerwincui
* @date 2022-02-07
*/
public interface IOauthClientDetailsService
{
/**
* 查询云云对接
*
* @param id 云云对接主键
* @return 云云对接
*/
public OauthClientDetails selectOauthClientDetailsById(Long id);
/**
* 查询云云对接列表
*
* @param oauthClientDetails 云云对接
* @return 云云对接集合
*/
public List<OauthClientDetails> selectOauthClientDetailsList(OauthClientDetails oauthClientDetails);
/**
* 新增云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
public AjaxResult insertOauthClientDetails(OauthClientDetails oauthClientDetails);
/**
* 修改云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
public AjaxResult updateOauthClientDetails(OauthClientDetails oauthClientDetails);
/**
* 批量删除云云对接
*
* @param ids 需要删除的云云对接主键集合
* @return 结果
*/
public int deleteOauthClientDetailsByIds(Long[] ids);
/**
* 删除云云对接信息
*
* @param clientId 云云对接主键
* @return 结果
*/
public int deleteOauthClientDetailsByClientId(String clientId);
default OauthClientDetails validOAuthClientFromCache(String clientId) {
return validOAuthClientFromCache(clientId, null, null, null, null);
}
OauthClientDetails validOAuthClientFromCache(String clientId, String clientSecret, String grantType, Collection<String> strings, String redirectUri);
}

View File

@ -0,0 +1,64 @@
package com.fastbee.oauth.service;
import com.fastbee.oauth.domain.OauthCode;
import java.util.List;
/**
* 【请填写功能名称】Service接口
*
* @author kerwincui
* @date 2024-03-20
*/
public interface IOauthCodeService
{
/**
* 查询【请填写功能名称】
*
* @param code 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
public OauthCode selectOauthCodeByCode(String code);
/**
* 查询【请填写功能名称】列表
*
* @param oauthCode 【请填写功能名称】
* @return 【请填写功能名称】集合
*/
public List<OauthCode> selectOauthCodeList(OauthCode oauthCode);
/**
* 新增【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
public int insertOauthCode(OauthCode oauthCode);
/**
* 修改【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
public int updateOauthCode(OauthCode oauthCode);
/**
* 批量删除【请填写功能名称】
*
* @param codes 需要删除的【请填写功能名称】主键集合
* @return 结果
*/
public int deleteOauthCodeByCodes(String[] codes);
/**
* 删除【请填写功能名称】信息
*
* @param code 【请填写功能名称】主键
* @return 结果
*/
public int deleteOauthCodeByCode(String code);
OauthCode consumeAuthorizationCode(String code);
}

View File

@ -0,0 +1,21 @@
package com.fastbee.oauth.service;
import com.fastbee.oauth.domain.OauthAccessToken;
import com.fastbee.oauth.domain.OauthClientDetails;
/**
* @author fastb
* @date 2023-09-01 17:20
*/
public interface OauthAccessTokenService {
String selectUserNameByTokenId(String token);
OauthAccessToken selectByTokenId(String tokenId);
void updateOpenIdByTokenId(String tokenId, String openUid);
OauthAccessToken selectByUserName(String userName);
OauthAccessToken grantAuthorizationCodeForAccessToken(OauthClientDetails client, String code, String redirectUri, String state);
}

View File

@ -0,0 +1,68 @@
package com.fastbee.oauth.service.impl;
import cn.hutool.core.util.IdUtil;
import com.fastbee.oauth.domain.OauthAccessToken;
import com.fastbee.oauth.domain.OauthClientDetails;
import com.fastbee.oauth.domain.OauthCode;
import com.fastbee.oauth.mapper.OauthAccessTokenMapper;
import com.fastbee.oauth.service.IOauthClientDetailsService;
import com.fastbee.oauth.service.IOauthCodeService;
import com.fastbee.oauth.service.OauthAccessTokenService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
* @author fastb
* @date 2023-09-01 17:20
*/
@Service
public class OauthAccessTokenServiceImpl implements OauthAccessTokenService {
@Resource
private OauthAccessTokenMapper oauthAccessTokenMapper;
@Resource
private IOauthCodeService oauthCodeService;
@Resource
private IOauthClientDetailsService oauthClientDetailsService;
@Override
public String selectUserNameByTokenId(String tokenId) {
return oauthAccessTokenMapper.selectUserNameByTokenId(tokenId);
}
@Override
public OauthAccessToken selectByTokenId(String tokenId) {
return oauthAccessTokenMapper.selectByTokenId(tokenId);
}
@Override
public void updateOpenIdByTokenId(String tokenId, String openUid) {
oauthAccessTokenMapper.updateOpenIdByTokenId(tokenId, openUid);
}
@Override
public OauthAccessToken selectByUserName(String userName) {
return oauthAccessTokenMapper.selectByUserName(userName);
}
@Override
public OauthAccessToken grantAuthorizationCodeForAccessToken(OauthClientDetails client, String code, String redirectUri, String state) {
OauthCode oauthCode = oauthCodeService.consumeAuthorizationCode(code);
oauthAccessTokenMapper.deleteByUserId(oauthCode.getUserId());
OauthAccessToken oauthAccessToken = new OauthAccessToken();
oauthAccessToken.setTokenId(generateRefreshToken());
oauthAccessToken.setClientId(client.getClientId());
oauthAccessToken.setUserId(oauthCode.getUserId());
oauthAccessToken.setRefreshToken(generateRefreshToken());
oauthAccessToken.setExpiresTime(LocalDateTime.now().plusSeconds(client.getAccessTokenValidity()));
oauthAccessTokenMapper.insertOauthAccessToken(oauthAccessToken);
return oauthAccessToken;
}
private static String generateRefreshToken() {
return IdUtil.fastSimpleUUID();
}
}

View File

@ -0,0 +1,171 @@
package com.fastbee.oauth.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.date.DateUtils;
import com.fastbee.oauth.domain.OauthApprovals;
import com.fastbee.oauth.domain.OauthClientDetails;
import com.fastbee.oauth.mapper.OauthApprovalsMapper;
import com.fastbee.oauth.service.IOauthApprovalsService;
import com.fastbee.oauth.service.IOauthClientDetailsService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 【请填写功能名称】Service业务层处理
*
* @author kerwincui
* @date 2024-03-20
*/
@Service
public class OauthApprovalsServiceImpl implements IOauthApprovalsService
{
/**
* 批准的过期时间,默认 30 天
*/
private static final Integer TIMEOUT = 30 * 24 * 60 * 60; // 单位:秒
@Resource
private OauthApprovalsMapper oauthApprovalsMapper;
@Resource
private IOauthClientDetailsService oauthClientDetailsService;
/**
* 查询【请填写功能名称】
*
* @param userid 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
@Override
public OauthApprovals selectOauthApprovalsByUserid(String userid)
{
return oauthApprovalsMapper.selectOauthApprovalsByUserid(userid);
}
/**
* 查询【请填写功能名称】列表
*
* @param oauthApprovals 【请填写功能名称】
* @return 【请填写功能名称】
*/
@Override
public List<OauthApprovals> selectOauthApprovalsList(OauthApprovals oauthApprovals)
{
return oauthApprovalsMapper.selectOauthApprovalsList(oauthApprovals);
}
/**
* 新增【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
@Override
public int insertOauthApprovals(OauthApprovals oauthApprovals)
{
return oauthApprovalsMapper.insertOauthApprovals(oauthApprovals);
}
/**
* 修改【请填写功能名称】
*
* @param oauthApprovals 【请填写功能名称】
* @return 结果
*/
@Override
public int updateOauthApprovals(OauthApprovals oauthApprovals)
{
return oauthApprovalsMapper.updateOauthApprovals(oauthApprovals);
}
/**
* 批量删除【请填写功能名称】
*
* @param userids 需要删除的【请填写功能名称】主键
* @return 结果
*/
@Override
public int deleteOauthApprovalsByUserids(String[] userids)
{
return oauthApprovalsMapper.deleteOauthApprovalsByUserids(userids);
}
/**
* 删除【请填写功能名称】信息
*
* @param userid 【请填写功能名称】主键
* @return 结果
*/
@Override
public int deleteOauthApprovalsByUserid(String userid)
{
return oauthApprovalsMapper.deleteOauthApprovalsByUserid(userid);
}
@Override
public boolean checkForPreApproval(Long userId, String clientId, Set<String> requestedScopes) {
OauthClientDetails oauthClientDetails = oauthClientDetailsService.validOAuthClientFromCache(clientId);
Assert.notNull(oauthClientDetails, "客户端不能为空"); // 防御性编程
List<String> strings = StringUtils.str2List(oauthClientDetails.getScope(), ",", true, true);
if (CollUtil.containsAll(strings, requestedScopes)) {
// gh-877 - if all scopes are auto approved, approvals still need to be added to the approval store.
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT);
for (String scope : requestedScopes) {
saveApprove(userId, clientId, scope, true, expireTime);
}
return true;
}
// 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true
List<OauthApprovals> approvalsList = this.getApproveList(userId, clientId);
Set<String> scopes = approvalsList.stream().filter(a -> "true".equals(a.getStatus())).map(OauthApprovals::getScope).collect(Collectors.toSet());
return CollUtil.containsAll(scopes, requestedScopes);
}
@Override
public boolean updateAfterApproval(Long userId, String clientId, Map<String, Boolean> requestedScopes) {
// 如果 requestedScopes 为空,说明没有要求,则返回 true 通过
if (CollUtil.isEmpty(requestedScopes)) {
return true;
}
// 更新批准的信息
boolean success = false; // 需要至少有一个同意
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT);
for (Map.Entry<String, Boolean> entry : requestedScopes.entrySet()) {
if (entry.getValue()) {
success = true;
}
saveApprove(userId, clientId, entry.getKey(), entry.getValue(), expireTime);
}
return success;
}
public List<OauthApprovals> getApproveList(Long userId, String clientId) {
List<OauthApprovals> approvalsList = oauthApprovalsMapper.selectListByUserIdAndClientId(
userId, clientId);
approvalsList.removeIf(o -> DateUtils.isExpired(o.getExpiresat()));
return approvalsList;
}
private void saveApprove(Long userId, String clientId, String scope, boolean b, LocalDateTime expireTime) {
// 先更新
OauthApprovals oauthApprovals = new OauthApprovals();
oauthApprovals.setUserid(userId.toString());
oauthApprovals.setClientid(clientId);
oauthApprovals.setScope(scope);
oauthApprovals.setStatus(String.valueOf(b));
oauthApprovals.setExpiresat(expireTime);
if (oauthApprovalsMapper.update(oauthApprovals) == 1) {
return;
}
// 失败,则说明不存在,进行更新
oauthApprovalsMapper.insertOauthApprovals(oauthApprovals);
}
}

View File

@ -0,0 +1,158 @@
package com.fastbee.oauth.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.oauth.domain.OauthClientDetails;
import com.fastbee.oauth.mapper.OauthClientDetailsMapper;
import com.fastbee.oauth.service.IOauthClientDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import static com.fastbee.common.utils.SecurityUtils.getLoginUser;
/**
* 云云对接Service业务层处理
*
* @author kerwincui
* @date 2022-02-07
*/
@Service
public class OauthClientDetailsServiceImpl implements IOauthClientDetailsService
{
@Autowired
private OauthClientDetailsMapper oauthClientDetailsMapper;
/**
* 查询云云对接
*
* @param id 云云对接主键
* @return 云云对接
*/
@Override
public OauthClientDetails selectOauthClientDetailsById(Long id)
{
return oauthClientDetailsMapper.selectOauthClientDetailsById(id);
}
/**
* 查询云云对接列表
*
* @param oauthClientDetails 云云对接
* @return 云云对接
*/
@Override
public List<OauthClientDetails> selectOauthClientDetailsList(OauthClientDetails oauthClientDetails)
{
// 查询所属机构
SysUser user = getLoginUser().getUser();
oauthClientDetails.setTenantId(user.getDept().getDeptUserId());
return oauthClientDetailsMapper.selectOauthClientDetailsList(oauthClientDetails);
}
/**
* 新增云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
@Override
public AjaxResult insertOauthClientDetails(OauthClientDetails oauthClientDetails)
{
SysUser user = getLoginUser().getUser();
if (null == user.getDept() || null == user.getDept().getDeptUserId()) {
throw new ServiceException("只允许租户配置");
}
oauthClientDetails.setTenantId(user.getDept().getDeptUserId());
oauthClientDetails.setTenantName(user.getDept().getDeptUserName());
OauthClientDetails oauthClientDetails1 = oauthClientDetailsMapper.selectOauthClientDetailsByType(oauthClientDetails.getType(), oauthClientDetails.getTenantId());
if (oauthClientDetails1 != null) {
return AjaxResult.error("同一个授权平台只能配置一条信息,请勿重复配置");
}
OauthClientDetails oauthClientDetails2 = oauthClientDetailsMapper.selectOauthClientDetailsByClientId(oauthClientDetails.getClientId());
if (oauthClientDetails2 != null) {
return AjaxResult.error("客户端id" + oauthClientDetails.getClientId() + "已存在");
}
return oauthClientDetailsMapper.insertOauthClientDetails(oauthClientDetails) > 0 ? AjaxResult.success() : AjaxResult.error();
}
/**
* 修改云云对接
*
* @param oauthClientDetails 云云对接
* @return 结果
*/
@Override
public AjaxResult updateOauthClientDetails(OauthClientDetails oauthClientDetails)
{
OauthClientDetails oauthClientDetails1 = oauthClientDetailsMapper.selectOauthClientDetailsByClientId(oauthClientDetails.getClientId());
if (oauthClientDetails1 != null && !Objects.equals(oauthClientDetails1.getId(), oauthClientDetails.getId())) {
return AjaxResult.error("客户端id" + oauthClientDetails.getClientId() + "已存在");
}
return oauthClientDetailsMapper.updateOauthClientDetails(oauthClientDetails) > 0 ? AjaxResult.success() : AjaxResult.error();
}
/**
* 批量删除云云对接
*
* @param ids 需要删除的云云对接主键
* @return 结果
*/
@Override
public int deleteOauthClientDetailsByIds(Long[] ids)
{
return oauthClientDetailsMapper.deleteOauthClientDetailsByIds(ids);
}
/**
* 删除云云对接信息
*
* @param clientId 云云对接主键
* @return 结果
*/
@Override
public int deleteOauthClientDetailsByClientId(String clientId)
{
return oauthClientDetailsMapper.deleteOauthClientDetailsByClientId(clientId);
}
@Override
public OauthClientDetails validOAuthClientFromCache(String clientId, String clientSecret, String grantType, Collection<String> scopes, String redirectUri) {
// 校验客户端存在、且开启
OauthClientDetails client = this.getOAuth2ClientFromCache(clientId);
if (client == null) {
throw new ServiceException("OAuth2 客户端不存在");
}
if (0 != client.getStatus()) {
throw new ServiceException("OAuth2 客户端已禁用");
}
// 校验客户端密钥
if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getClientSecret(), clientSecret)) {
throw new ServiceException("无效 client_secret");
}
// 校验授权方式
// if (StrUtil.isNotEmpty(grantType) && !StringUtils.contains(client.getAuthorizedGrantTypes(), grantType)) {
// throw new ServiceException("不支持该授权类型");
// }
// 校验授权范围
// if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(client.getScope(), scopes)) {
// throw new ServiceException("授权范围过大");
// }
// 校验回调地址
if (StrUtil.isNotEmpty(redirectUri) && !redirectUri.equals(client.getWebServerRedirectUri())) {
throw new ServiceException("无效 redirect_uri:" + redirectUri);
}
return client;
}
private OauthClientDetails getOAuth2ClientFromCache(String clientId) {
return oauthClientDetailsMapper.selectOauthClientDetailsByClientId(clientId);
}
}

View File

@ -0,0 +1,108 @@
package com.fastbee.oauth.service.impl;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.oauth.domain.OauthCode;
import com.fastbee.oauth.mapper.OauthCodeMapper;
import com.fastbee.oauth.service.IOauthCodeService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 【请填写功能名称】Service业务层处理
*
* @author kerwincui
* @date 2024-03-20
*/
@Service
public class OauthCodeServiceImpl implements IOauthCodeService
{
@Resource
private OauthCodeMapper oauthCodeMapper;
/**
* 查询【请填写功能名称】
*
* @param code 【请填写功能名称】主键
* @return 【请填写功能名称】
*/
@Override
public OauthCode selectOauthCodeByCode(String code)
{
return oauthCodeMapper.selectOauthCodeByCode(code);
}
/**
* 查询【请填写功能名称】列表
*
* @param oauthCode 【请填写功能名称】
* @return 【请填写功能名称】
*/
@Override
public List<OauthCode> selectOauthCodeList(OauthCode oauthCode)
{
return oauthCodeMapper.selectOauthCodeList(oauthCode);
}
/**
* 新增【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
@Override
public int insertOauthCode(OauthCode oauthCode)
{
return oauthCodeMapper.insertOauthCode(oauthCode);
}
/**
* 修改【请填写功能名称】
*
* @param oauthCode 【请填写功能名称】
* @return 结果
*/
@Override
public int updateOauthCode(OauthCode oauthCode)
{
return oauthCodeMapper.updateOauthCode(oauthCode);
}
/**
* 批量删除【请填写功能名称】
*
* @param codes 需要删除的【请填写功能名称】主键
* @return 结果
*/
@Override
public int deleteOauthCodeByCodes(String[] codes)
{
return oauthCodeMapper.deleteOauthCodeByCodes(codes);
}
/**
* 删除【请填写功能名称】信息
*
* @param code 【请填写功能名称】主键
* @return 结果
*/
@Override
public int deleteOauthCodeByCode(String code)
{
return oauthCodeMapper.deleteOauthCodeByCode(code);
}
@Override
public OauthCode consumeAuthorizationCode(String code) {
OauthCode oauthCode = this.selectOauthCodeByCode(code);
if (oauthCode == null) {
throw new ServiceException("code 不存在");
}
// if (DateUtils.isExpired(codeDO.getExpiresTime())) {
// throw exception(OAUTH2_CODE_EXPIRE);
// }
this.deleteOauthCodeByCode(code);
return oauthCode;
}
}

View File

@ -0,0 +1,126 @@
package com.fastbee.oauth.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.TableMap;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map;
/**
* HTTP 工具类
*
* @author 芋道源码
*/
public class HttpUtils {
@SuppressWarnings("unchecked")
public static String replaceUrlQuery(String url, String key, String value) {
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());
// 先移除
TableMap<CharSequence, CharSequence> query = (TableMap<CharSequence, CharSequence>)
ReflectUtil.getFieldValue(builder.getQuery(), "query");
query.remove(key);
// 后添加
builder.addQuery(key, value);
return builder.build();
}
private String append(String base, Map<String, ?> query, boolean fragment) {
return append(base, query, null, fragment);
}
/**
* 拼接 URL
*
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法
*
* @param base 基础 URL
* @param query 查询参数
* @param keys query 的 key对应的原本的 key 的映射。例如说 query 里有个 key 是 xx实际它的 key 是 extra_xx则通过 keys 里添加这个映射
* @param fragment URL 的 fragment即拼接到 # 中
* @return 拼接后的 URL
*/
public static String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
UriComponentsBuilder template = UriComponentsBuilder.newInstance();
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
URI redirectUri;
try {
// assume it's encoded to start with (if it came in over the wire)
redirectUri = builder.build(true).toUri();
} catch (Exception e) {
// ... but allow client registrations to contain hard-coded non-encoded values
redirectUri = builder.build().toUri();
builder = UriComponentsBuilder.fromUri(redirectUri);
}
template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
.userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
if (fragment) {
StringBuilder values = new StringBuilder();
if (redirectUri.getFragment() != null) {
String append = redirectUri.getFragment();
values.append(append);
}
for (String key : query.keySet()) {
if (values.length() > 0) {
values.append("&");
}
String name = key;
if (keys != null && keys.containsKey(key)) {
name = keys.get(key);
}
values.append(name).append("={").append(key).append("}");
}
if (values.length() > 0) {
template.fragment(values.toString());
}
UriComponents encoded = template.build().expand(query).encode();
builder.fragment(encoded.getFragment());
} else {
for (String key : query.keySet()) {
String name = key;
if (keys != null && keys.containsKey(key)) {
name = keys.get(key);
}
template.queryParam(name, "{" + key + "}");
}
template.fragment(redirectUri.getFragment());
UriComponents encoded = template.build().expand(query).encode();
builder.query(encoded.getQuery());
}
return builder.build().toUriString();
}
public static String[] obtainBasicAuthorization(HttpServletRequest request) {
String clientId;
String clientSecret;
// 先从 Header 中获取
String authorization = request.getHeader("Authorization");
authorization = StrUtil.subAfter(authorization, "Basic ", true);
if (StringUtils.hasText(authorization)) {
authorization = Base64.decodeStr(authorization);
clientId = StrUtil.subBefore(authorization, ":", false);
clientSecret = StrUtil.subAfter(authorization, ":", false);
// 再从 Param 中获取
} else {
clientId = request.getParameter("client_id");
clientSecret = request.getParameter("client_secret");
}
// 如果两者非空,则返回
if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) {
return new String[]{clientId, clientSecret};
}
return null;
}
}

View File

@ -0,0 +1,101 @@
package com.fastbee.oauth.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
/**
* OAuth2 相关的工具类
*
* @author 芋道源码
*/
public class OAuth2Utils {
/**
* 构建授权码模式下,重定向的 URI
*
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法
*
* @param redirectUri 重定向 URI
* @param authorizationCode 授权码
* @param state 状态
* @return 授权码模式下的重定向 URI
*/
public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) {
Map<String, String> query = new LinkedHashMap<>();
query.put("code", authorizationCode);
if (state != null) {
query.put("state", state);
}
return HttpUtils.append(redirectUri, query, null, false);
}
/**
* 构建简化模式下,重定向的 URI
*
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 appendAccessToken 方法
*
* @param redirectUri 重定向 URI
* @param accessToken 访问令牌
* @param state 状态
* @param expireTime 过期时间
* @param scopes 授权范围
* @param additionalInformation 附加信息
* @return 简化授权模式下的重定向 URI
*/
public static String buildImplicitRedirectUri(String redirectUri, String accessToken, String state, LocalDateTime expireTime,
Collection<String> scopes, Map<String, Object> additionalInformation) {
Map<String, Object> vars = new LinkedHashMap<String, Object>();
Map<String, String> keys = new HashMap<String, String>();
vars.put("access_token", accessToken);
vars.put("token_type", "bearer");
if (state != null) {
vars.put("state", state);
}
if (expireTime != null) {
vars.put("expires_in", getExpiresIn(expireTime));
}
if (CollUtil.isNotEmpty(scopes)) {
vars.put("scope", buildScopeStr(scopes));
}
if (CollUtil.isNotEmpty(additionalInformation)) {
for (String key : additionalInformation.keySet()) {
Object value = additionalInformation.get(key);
if (value != null) {
keys.put("extra_" + key, key);
vars.put("extra_" + key, value);
}
}
}
// Do not include the refresh token (even if there is one)
return HttpUtils.append(redirectUri, vars, keys, true);
}
public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state,
String error, String description) {
Map<String, String> query = new LinkedHashMap<String, String>();
query.put("error", error);
query.put("error_description", description);
if (state != null) {
query.put("state", state);
}
return HttpUtils.append(redirectUri, query, null, !responseType.contains("code"));
}
public static long getExpiresIn(LocalDateTime expireTime) {
return LocalDateTimeUtil.between(LocalDateTime.now(), expireTime, ChronoUnit.SECONDS);
}
public static String buildScopeStr(Collection<String> scopes) {
return CollUtil.join(scopes, " ");
}
public static List<String> buildScopes(String scope) {
return StrUtil.split(scope, ' ');
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.oauth.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenAccessTokenRespVO {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("expires_in")
private Long expiresIn;
private String scope;
}

View File

@ -0,0 +1,33 @@
package com.fastbee.oauth.vo;
import com.fastbee.common.core.text.KeyValue;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenAuthorizeInfoRespVO {
/**
* 客户端
*/
private Client client;
private List<KeyValue<String, Boolean>> scopes;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Client {
private String name;
private String logo;
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.oauth.vo;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @author fastb
* @version 1.0
* @description: TODO
* @date 2024-03-21 11:13
*/
@Data
public class Oauth2AccessTokenReqVO {
@JSONField(name = "grant_type")
private String grantType;
private String code;
@JSONField(name = "redirect_uri")
private String redirectUri;
private String scope;
private String state;
}