第一次提交
This commit is contained in:
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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, ' ');
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
Reference in New Issue
Block a user