This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

54
blade-core-oauth2/pom.xml Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-core-oauth2</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-social</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
<!-- captcha -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
</dependency>
<!-- session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,94 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.config;
import lombok.AllArgsConstructor;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.oauth2.granter.TokenGranter;
import org.springblade.core.oauth2.granter.TokenGranterEnhancer;
import org.springblade.core.oauth2.granter.TokenGranterFactory;
import org.springblade.core.oauth2.handler.*;
import org.springblade.core.oauth2.props.OAuth2Properties;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.service.impl.OAuth2ClientDetailService;
import org.springblade.core.oauth2.service.impl.OAuth2UserDetailService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
* OAuth2Configuration
*
* @author BladeX
*/
@AllArgsConstructor
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(OAuth2Properties.class)
@ConditionalOnProperty(value = OAuth2Properties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class OAuth2AutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AuthorizationHandler authorizationHandler() {
return new OAuth2AuthorizationHandler();
}
@Bean
@ConditionalOnMissingBean
public PasswordHandler passwordHandler() {
return new OAuth2PasswordHandler();
}
@Bean
@ConditionalOnMissingBean
public TokenHandler tokenHandler(JwtProperties properties) {
return new OAuth2TokenHandler(properties);
}
@Bean
@ConditionalOnMissingBean
public OAuth2ClientService oAuth2ClientService(JdbcTemplate jdbcTemplate) {
return new OAuth2ClientDetailService(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean
public OAuth2UserService oAuth2UserService(JdbcTemplate jdbcTemplate) {
return new OAuth2UserDetailService(jdbcTemplate);
}
@Bean
public TokenGranterFactory tokenGranterFactory(List<TokenGranter> tokenGranters, List<TokenGranterEnhancer> tokenGranterEnhancers, OAuth2Properties properties) {
return new TokenGranterFactory(tokenGranters, tokenGranterEnhancers, properties);
}
}

View File

@@ -0,0 +1,46 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* OAuth资源配置
*
* @author BladeX
*/
@AutoConfiguration
public class OAuth2WebConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}

View File

@@ -0,0 +1,79 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* OAuth2AuthorizationConstant
*
* @author BladeX
*/
public interface OAuth2AuthorizationConstant {
/**
* 用户session key
*/
String AUTHORIZATION_SESSION_KEY = "user";
/**
* 授权请求key
*/
String AUTHORIZATION_REQUEST_KEY = "authorizationRequest";
/**
* 跳转参数
*/
String REDIRECT_URL = "redirect:";
/**
* 授权地址
*/
String AUTHORIZE_URL = "/oauth/authorize";
/**
* 登录地址
*/
String LOGIN_URL = "/oauth/login";
/**
* 错误地址
*/
String ERROR_URL = "/oauth/error";
/**
* 授权视图
*/
String AUTHORIZE_MODEL = "authorize";
/**
* 登录视图
*/
String LOGIN_MODEL = "login";
/**
* 错误视图
*/
String ERROR_MODEL = "error";
}

View File

@@ -0,0 +1,54 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* OAuth2ClientConstant
*
* @author BladeX
*/
public interface OAuth2ClientConstant {
/**
* blade_client表字段
*/
String CLIENT_FIELDS = "id, client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove";
/**
* blade_client查询语句
*/
String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
/**
* blade_client查询排序
*/
String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
/**
* 查询client_id
*/
String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
}

View File

@@ -0,0 +1,76 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* GranterTypeConstant
*
* @author BladeX
*/
public interface OAuth2GranterConstant {
/**
* 授权码模式
*/
String AUTHORIZATION_CODE = "authorization_code";
/**
* 密码模式
*/
String PASSWORD = "password";
/**
* 刷新token模式
*/
String REFRESH_TOKEN = "refresh_token";
/**
* 客户端模式
*/
String CLIENT_CREDENTIALS = "client_credentials";
/**
* 简化模式
*/
String IMPLICIT = "implicit";
/**
* 验证码模式
*/
String CAPTCHA = "captcha";
/**
* 手机验证码模式
*/
String SMS_CODE = "sms_code";
/**
* 微信小程序模式
*/
String WECHAT_APPLET = "wechat_applet";
/**
* 开放平台模式
*/
String SOCIAL = "social";
/**
* 注册模式
*/
String REGISTER = "register";
}

View File

@@ -0,0 +1,103 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* OAuth2常量.
*
* @author BladeX
*/
public interface OAuth2ParameterConstant {
/**
* 客户端id
*/
String CLIENT_ID = "client_id";
/**
* 客户端密钥
*/
String CLIENT_SECRET = "client_secret";
/**
* 令牌
*/
String ACCESS_TOKEN = "access_token";
/**
* 刷新令牌
*/
String REFRESH_TOKEN = "refresh_token";
/**
* 租户编号
*/
String TENANT_ID = "tenant_id";
/**
* 用户名字
*/
String NAME = "name";
/**
* 用户名
*/
String USERNAME = "username";
/**
* 密码
*/
String PASSWORD = "password";
/**
* 手机号
*/
String PHONE = "phone";
/**
* 电子游戏
*/
String EMAIL = "email";
/**
* 授权类型
*/
String GRANT_TYPE = "grant_type";
/**
* 响应类型
*/
String SCOPE = "scope";
/**
* 重定向地址
*/
String REDIRECT_URI = "redirect_uri";
/**
* 返回类型
*/
String RESPONSE_TYPE = "response_type";
/**
* 状态
*/
String STATE = "state";
/**
* 验证
*/
String CODE = "code";
/**
* 来源
*/
String SOURCE = "source";
}

View File

@@ -0,0 +1,37 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* OAuth2ResponseConstant
*
* @author BladeX
*/
public interface OAuth2ResponseConstant {
String SUCCESS = "success";
String ERROR_CODE = "error_code";
String ERROR_DESCRIPTION = "error_description";
}

View File

@@ -0,0 +1,57 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* TokenConstant
*
* @author BladeX
*/
public interface OAuth2TokenConstant {
String HEADER_AUTHORIZATION = "Authorization";
String HEADER_AUTHORIZATION_PREFIX = "Basic ";
String TOKEN_HEADER = "Blade-Auth";
String TENANT_HEADER = "Tenant-Id";
String DEFAULT_TENANT_ID = "000000";
String USER_HEADER = "User-Id";
String DEPT_HEADER = "Dept-Id";
String ROLE_HEADER = "Role-Id";
String USER_TYPE_HEADER = "User-Type";
String DEFAULT_USER_TYPE = "web";
String USER_FAIL_KEY = "blade:user::blade:fail:";
String CAPTCHA_CACHE_KEY = "blade:auth::blade:captcha:";
String CAPTCHA_HEADER_KEY = "Captcha-Key";
String CAPTCHA_HEADER_CODE = "Captcha-Code";
String CAPTCHA_NOT_CORRECT = "验证码不正确";
String TOKEN_NOT_PERMISSION = "令牌授权已过期";
String USER_NOT_FOUND = "用户名或密码错误";
String USER_HAS_NO_ROLE = "未获得用户的角色信息";
String USER_HAS_NO_TENANT = "未获得用户的租户信息";
String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
String USER_HAS_TOO_MANY_FAILS = "登录错误次数过多,请稍后再试";
String DEFAULT_AVATAR = "https://bladex.cn/images/logo.png";
}

View File

@@ -0,0 +1,45 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.constant;
/**
* OAuth2UserConstant
*
* @author BladeX
*/
public interface OAuth2UserConstant {
/**
* blade_user查询
*/
String DEFAULT_USERID_SELECT_STATEMENT = "select id as user_id, tenant_id , account, password from blade_user where id = ?";
/**
* blade_user查询
*/
String DEFAULT_USERNAME_SELECT_STATEMENT = "select id as user_id, tenant_id , account, password from blade_user where account = ?";
}

View File

@@ -0,0 +1,266 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.endpoint;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springblade.core.oauth2.constant.OAuth2AuthorizationConstant;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2AuthorizationRequest;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.utils.OAuth2CodeUtil;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.time.Duration;
import java.util.Optional;
import static org.springblade.core.oauth2.constant.OAuth2ParameterConstant.*;
/**
* AuthorizationEndpoint
*
* @author BladeX
*/
@Controller
@RequiredArgsConstructor
@Tag(name = "用户授权码模式认证", description = "2 - OAuth2授权码模式端点")
public class OAuth2AuthorizationEndpoint implements OAuth2AuthorizationConstant {
private final BladeRedis bladeRedis;
private final OAuth2ClientService clientService;
private final OAuth2UserService userService;
private final PasswordHandler passwordHandler;
@GetMapping("/oauth/login")
public String loginPage(HttpSession session, Model model) {
// 从session中获取授权请求参数
Optional.ofNullable((OAuth2AuthorizationRequest) session.getAttribute(AUTHORIZATION_REQUEST_KEY))
.ifPresent(authorizationRequest -> model.addAttribute(AUTHORIZATION_REQUEST_KEY, authorizationRequest));
// 返回登录页面视图
return LOGIN_MODEL;
}
@PostMapping("/oauth/login/perform")
public String performLogin(@SessionAttribute(AUTHORIZATION_REQUEST_KEY) OAuth2AuthorizationRequest authorizationRequest,
RedirectAttributes redirectAttributes, HttpSession session) {
// 根据用户名和密码验证用户
return Optional.ofNullable(authenticateUser(session, authorizationRequest))
.map(user -> {
// 用户验证成功,处理授权请求参数和重定向
authorizationRequest.setTenantId(user.getTenantId());
session.setAttribute(AUTHORIZATION_REQUEST_KEY, authorizationRequest);
redirectAttributes.addAllAttributes(authorizationRequest.getParameters());
return REDIRECT_URL + AUTHORIZE_URL; // 重定向回授权视图
})
.orElse(REDIRECT_URL + ERROR_URL); // 用户验证失败,重定向回失败视图
}
@GetMapping("/oauth/authorize")
public String authorize(@SessionAttribute(value = AUTHORIZATION_REQUEST_KEY, required = false) OAuth2AuthorizationRequest authorizationRequest,
HttpSession session, Model model) {
// 获取授权请求参数
OAuth2AuthorizationRequest request = OAuth2AuthorizationRequest.create().buildParameters();
// 设置请求参数
Optional.ofNullable(authorizationRequest).ifPresentOrElse(authReq -> {
if (request.getState() == null) {
request.setState(authReq.getState());
}
if (request.getClientId() != null) {
session.setAttribute(AUTHORIZATION_REQUEST_KEY, request);
}
}, () -> session.setAttribute(AUTHORIZATION_REQUEST_KEY, request));
// 获取用户信息并跳转
return Optional.ofNullable(session.getAttribute(AUTHORIZATION_SESSION_KEY))
.map(obj -> (OAuth2User) obj)
.map(user -> {
model.addAttribute(USERNAME, user.getAccount());
model.addAllAttributes(request.getParameters());
return AUTHORIZE_MODEL; // 用户已登录,显示授权页面
})
.orElse(REDIRECT_URL + LOGIN_URL); // 用户未登录,重定向到登录页面
}
@PostMapping("/oauth/authorize/perform")
public String performAuthorize(@RequestParam Boolean approval,
@RequestParam(required = false) String state,
@SessionAttribute(AUTHORIZATION_REQUEST_KEY) OAuth2AuthorizationRequest authorizationRequest,
RedirectAttributes redirectAttributes, HttpSession session) {
// 此处可以添加用户同意授权的处理逻辑
if (!approval) {
// 用户拒绝授权,返回授权页面
return REDIRECT_URL + LOGIN_URL;
}
// 获取客户端信息
OAuth2Client client = clientService.loadByClientId(authorizationRequest.getClientId());
// 校验回调地址信息
if (!clientService.validateRedirectUri(client, authorizationRequest.getRedirectUri())) {
// 重定向URI参数不匹配返回错误页面
return ERROR_MODEL;
}
// 生成授权码
String code = createCode();
// 设置用户信息
OAuth2User user = (OAuth2User) session.getAttribute(AUTHORIZATION_SESSION_KEY);
if (user == null) {
// 用户未登录,重定向到登录页面
return REDIRECT_URL + LOGIN_URL;
}
// 校验state参数
if (Func.equalsSafe(authorizationRequest.getState(), state)) {
// 保存授权码
saveCode(code, user);
} else {
// 重定向URI和state参数不匹配返回错误页面
return ERROR_MODEL;
}
// 使用RedirectAttributes添加授权码和state参数
redirectAttributes.addAttribute(CODE, code);
// 添加tenantId参数为state参数
if (authorizationRequest.getTenantId() != null) {
redirectAttributes.addAttribute(STATE, authorizationRequest.getTenantId());
}
// 用户自定义state参数则覆盖
if (state != null) {
redirectAttributes.addAttribute(STATE, state);
}
// 重定向到客户端提供的重定向URI
return REDIRECT_URL + authorizationRequest.getRedirectUri();
}
@GetMapping("/oauth/authorize/logout")
public String logout(HttpSession session) {
// 退出登录清除session中的用户信息
session.removeAttribute(AUTHORIZATION_SESSION_KEY);
return REDIRECT_URL + LOGIN_URL;
}
@GetMapping("/oauth/error")
public String error() {
// 返回错误页面
return ERROR_MODEL;
}
private OAuth2User authenticateUser(HttpSession session, OAuth2AuthorizationRequest authorizationRequest) {
// 创建 OAuth2 请求对象并构建参数
OAuth2Request request = OAuth2Request.create().buildArgs();
// 获取请求参数
String username = request.getUsername();
String password = request.getPassword();
String clientId = authorizationRequest.getClientId();
String redirectUri = authorizationRequest.getRedirectUri();
// 获取客户端信息
OAuth2Client client = clientService.loadByClientId(clientId);
// 校验回调地址信息
if (!clientService.validateRedirectUri(client, redirectUri)) {
return null;
}
// 获取用户信息
OAuth2User user = userService.loadByUsername(username, request);
// 校验用户信息
if (!userService.validateUser(user)) {
return null;
}
// 校验用户密码
if (!passwordHandler.matches(password, user.getPassword())) {
return null;
}
// 将用户信息存入session
session.setAttribute(AUTHORIZATION_SESSION_KEY, user);
// 返回用户信息
return user;
}
/**
* 授权码模式获取授权码
*
* @return 授权码
*/
private String createCode() {
// 生成6位随机数作为授权码
String code = StringUtil.random(6);
if (bladeRedis.exists(OAuth2CodeUtil.codeKey(code))) {
// 如果生成的授权码已存在,则递归调用重新生成
return createCode();
}
return code;
}
/**
* 保存code信息
*/
private void saveCode(String code, OAuth2User user) {
// 保存code信息到redis30分钟过期
bladeRedis.setEx(OAuth2CodeUtil.codeKey(code), user, Duration.ofMinutes(30));
}
/**
* 根据code获取用户信息
*
* @param code code
* @return 用户信息
*/
public OAuth2User getUserByCode(String code) {
// 根据code从redis中获取用户信息
return bladeRedis.get(OAuth2CodeUtil.codeKey(code));
}
}

View File

@@ -0,0 +1,102 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.endpoint;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springblade.core.social.props.SocialProperties;
import org.springblade.core.social.utils.SocialUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* 第三方登录端点
*
* @author BladeX
*/
@Slf4j
@RestController
@AllArgsConstructor
@ConditionalOnProperty(value = "social.enabled", havingValue = "true")
@Tag(name = "开放平台登录", description = "3 - 开放平台登录端点")
public class OAuth2SocialEndpoint {
private final SocialProperties socialProperties;
/**
* 授权完毕跳转
*/
@Operation(summary = "授权完毕跳转")
@RequestMapping("/oauth/render/{source}")
public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
response.sendRedirect(authorizeUrl);
}
/**
* 获取认证信息
*/
@Operation(summary = "获取认证信息")
@RequestMapping("/oauth/callback/{source}")
public Object login(@PathVariable("source") String source, AuthCallback callback) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.login(callback);
}
/**
* 撤销授权
*/
@Operation(summary = "撤销授权")
@RequestMapping("/oauth/revoke/{source}/{token}")
public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.revoke(AuthToken.builder().accessToken(token).build());
}
/**
* 续期accessToken
*/
@Operation(summary = "续期令牌")
@RequestMapping("/oauth/refresh/{source}")
public Object refreshAuth(@PathVariable("source") String source, String token) {
AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
}
}

View File

@@ -0,0 +1,170 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.endpoint;
import com.wf.captcha.SpecCaptcha;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.oauth2.constant.OAuth2ParameterConstant;
import org.springblade.core.oauth2.granter.TokenGranter;
import org.springblade.core.oauth2.granter.TokenGranterFactory;
import org.springblade.core.oauth2.handler.AuthorizationHandler;
import org.springblade.core.oauth2.handler.TokenHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.provider.OAuth2Validation;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.support.Kv;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.UUID;
import static org.springblade.core.cache.constant.CacheConstant.*;
import static org.springblade.core.oauth2.constant.OAuth2TokenConstant.CAPTCHA_CACHE_KEY;
/**
* OAuth2认证端点
*
* @author BladeX
*/
@RestController
@AllArgsConstructor
@Tag(name = "用户授权认证", description = "1 - OAuth2授权认证端点")
public class OAuth2TokenEndPoint {
private final BladeRedis bladeRedis;
private final JwtProperties jwtProperties;
private final TokenGranterFactory granterFactory;
private final AuthorizationHandler authorizationHandler;
private final TokenHandler tokenHandler;
@PostMapping("/oauth/token")
@Operation(
summary = "获取Token",
description = "OAuth2认证接口",
parameters = {
@Parameter(in = ParameterIn.QUERY, name = OAuth2ParameterConstant.USERNAME, description = "账号", schema = @Schema(type = "string")),
@Parameter(in = ParameterIn.QUERY, name = OAuth2ParameterConstant.PASSWORD, description = "密码", schema = @Schema(type = "string")),
@Parameter(in = ParameterIn.QUERY, name = OAuth2ParameterConstant.GRANT_TYPE, description = "授权类型", schema = @Schema(type = "string")),
@Parameter(in = ParameterIn.QUERY, name = OAuth2ParameterConstant.REFRESH_TOKEN, description = "刷新token", schema = @Schema(type = "string")),
@Parameter(in = ParameterIn.QUERY, name = OAuth2ParameterConstant.SCOPE, description = "权限范围", schema = @Schema(type = "string"))
}
)
public ResponseEntity<?> token() {
// 创建 OAuth2 请求对象并构建参数
OAuth2Request request = OAuth2Request.create().buildArgs();
// 根据请求的授权类型创建对应的 TokenGranter
TokenGranter tokenGranter = granterFactory.create(request.getGrantType());
// 使用 TokenGranter 获取用户信息
OAuth2User user = tokenGranter.user(request);
// 使用授权处理器对用户进行验证
OAuth2Validation validation = authorizationHandler.authValidation(user, request);
// 检查验证是否成功
if (!validation.isSuccess()) {
// 验证失败处理逻辑
authorizationHandler.authFailure(user, request, validation);
// 根据验证失败的错误代码抛出异常
OAuth2ExceptionUtil.throwFromCode(validation.getCode());
}
// 创建令牌
OAuth2Token token = tokenGranter.token(user, request);
// 对令牌进行增强处理
OAuth2Token enhanceToken = tokenHandler.enhance(user, token, request);
// 验证成功处理逻辑
authorizationHandler.authSuccessful(user, request);
// 返回增强后的令牌
return ResponseEntity.ok(enhanceToken.getArgs());
}
@GetMapping("/oauth/logout")
@Operation(summary = "退出登录")
public Kv logout() {
BladeUser user = AuthUtil.getUser();
if (user != null && jwtProperties.getState()) {
OAuth2Request request = OAuth2Request.create().buildHeaderArgs();
String token = JwtUtil.getToken(request.getToken());
JwtUtil.removeAccessToken(user.getTenantId(), user.getClientId(), String.valueOf(user.getUserId()), token);
}
return Kv.create().set("success", "true").set("msg", "success");
}
@GetMapping("/oauth/captcha")
@Operation(summary = "获取验证码")
public Kv captcha() {
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
String verCode = specCaptcha.text().toLowerCase();
String key = UUID.randomUUID().toString();
// 存入redis并设置过期时间为30分钟
bladeRedis.setEx(CAPTCHA_CACHE_KEY + key, verCode, Duration.ofMinutes(30));
// 将key和base64返回给前端
return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
}
@GetMapping("/oauth/clear-cache")
@Operation(summary = "清除缓存")
public Kv clearCache() {
CacheUtil.clear(BIZ_CACHE);
CacheUtil.clear(USER_CACHE);
CacheUtil.clear(DICT_CACHE);
CacheUtil.clear(FLOW_CACHE);
CacheUtil.clear(SYS_CACHE);
CacheUtil.clear(PARAM_CACHE);
CacheUtil.clear(RESOURCE_CACHE);
CacheUtil.clear(MENU_CACHE);
CacheUtil.clear(DICT_CACHE, Boolean.FALSE);
CacheUtil.clear(MENU_CACHE, Boolean.FALSE);
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
CacheUtil.clear(PARAM_CACHE, Boolean.FALSE);
return Kv.create().set("success", "true").set("msg", "success");
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 客户端认证失败
*
* @author BladeX
*/
public class ClientInvalidException extends OAuth2Exception {
public ClientInvalidException(String msg) {
super(ExceptionCode.INVALID_CLIENT, msg);
}
public ClientInvalidException(String msg, Throwable cause) {
super(ExceptionCode.INVALID_CLIENT, msg, cause);
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 客户端未找到
*
* @author BladeX
*/
public class ClientNotFoundException extends OAuth2Exception {
public ClientNotFoundException(String msg) {
super(ExceptionCode.CLIENT_NOT_FOUND, msg);
}
public ClientNotFoundException(String msg, Throwable cause) {
super(ExceptionCode.CLIENT_NOT_FOUND, msg, cause);
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 客户端未授权
*
* @author BladeX
*/
public class ClientUnauthorizedException extends OAuth2Exception {
public ClientUnauthorizedException(String msg) {
super(ExceptionCode.UNAUTHORIZED_CLIENT, msg);
}
public ClientUnauthorizedException(String msg, Throwable cause) {
super(ExceptionCode.UNAUTHORIZED_CLIENT, msg, cause);
}
}

View File

@@ -0,0 +1,150 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
import static org.springblade.core.oauth2.exception.OAuth2ErrorMessage.INVALID_ERROR_CODE;
/**
* OAuth2ExceptionCode
*
* @author BladeX
*/
@Getter
@AllArgsConstructor
public enum ExceptionCode {
/**
* 无效请求 - 请求缺少必要的参数或格式不正确。
*/
INVALID_REQUEST(OAuth2ErrorCode.INVALID_REQUEST, "无效请求"),
/**
* 用户不存在 - 指定的用户ID不存在或无效。
*/
USER_NOT_FOUND(OAuth2ErrorCode.USER_NOT_FOUND, "用户不存在"),
/**
* 用户租户不存在 - 指定的用户租户未授权。
*/
USER_TENANT_NOT_FOUND(OAuth2ErrorCode.USER_TENANT_NOT_FOUND, "用户租户不存在"),
/**
* 用户登录失败次数过多 - 用户登录失败次数过多。
*/
USER_TOO_MANY_FAILS(OAuth2ErrorCode.USER_TOO_MANY_FAILS, "用户登录失败次数过多"),
/**
* 用户认证失败 - 指定的用户认证信息错误或无效。
*/
INVALID_USER(OAuth2ErrorCode.INVALID_USER, "认证信息错误或无效"),
/**
* 用户未授权 - 指定的用户未授权。
*/
UNAUTHORIZED_USER(OAuth2ErrorCode.UNAUTHORIZED_USER, "认证信息错误或无效"),
/**
* 用户租户未授权 - 指定的用户租户未授权。
*/
UNAUTHORIZED_USER_TENANT(OAuth2ErrorCode.UNAUTHORIZED_USER_TENANT, "用户租户未授权"),
/**
* 用户未授权 - 指定的用户未授权。
*/
INVALID_REFRESH_TOKEN(OAuth2ErrorCode.INVALID_REFRESH_TOKEN, "令牌刷新错误或无效"),
/**
* 客户端不存在 - 指定的客户端ID不存在或无效。
*/
CLIENT_NOT_FOUND(OAuth2ErrorCode.CLIENT_NOT_FOUND, "客户端不存在"),
/**
* 客户端认证失败 - 客户端提供的认证信息错误或无效。
*/
INVALID_CLIENT(OAuth2ErrorCode.INVALID_CLIENT, "客户端认证失败"),
/**
* 回调地址错误或无效 - 客户端回调地址错误或无效。
*/
INVALID_CLIENT_REDIRECT_URI(OAuth2ErrorCode.INVALID_CLIENT_REDIRECT_URI, "客户端未授权"),
/**
* 客户端未授权 - 客户端无权执行此操作。
*/
UNAUTHORIZED_CLIENT(OAuth2ErrorCode.UNAUTHORIZED_CLIENT, "客户端未授权"),
/**
* 不支持的授权类型 - 请求的授权类型不被服务器支持。
*/
UNSUPPORTED_GRANT_TYPE(OAuth2ErrorCode.UNSUPPORTED_GRANT_TYPE, "不支持的授权类型"),
/**
* 无效的授权类型 - 提供的授权令牌无效、过期或被撤销。
*/
INVALID_GRANTER(OAuth2ErrorCode.INVALID_GRANTER, "无效的授权类型"),
/**
* 无效的无效的授权范围 - 请求的无效的授权范围无效、未知或格式不正确。
*/
INVALID_SCOPE(OAuth2ErrorCode.INVALID_SCOPE, "授权范围"),
/**
* 服务器错误 - 服务器内部错误,无法完成请求。
*/
SERVER_ERROR(OAuth2ErrorCode.SERVER_ERROR, "服务器错误"),
/**
* 访问被拒绝 - 由于各种原因,服务器拒绝执行此操作。
*/
ACCESS_DENIED(OAuth2ErrorCode.ACCESS_DENIED, "访问被拒绝"),
/**
* 服务暂不可用 - 服务器暂时过载或维护,无法处理请求。
*/
TEMPORARILY_UNAVAILABLE(OAuth2ErrorCode.TEMPORARILY_UNAVAILABLE, "服务暂不可用");
final int code;
final String message;
/**
* 通过错误代码获取枚举
*
* @param code 错误代码
* @return ExceptionCodeEnum
*/
public static ExceptionCode of(int code) {
for (ExceptionCode value : ExceptionCode.values()) {
if (value.code == code) {
return value;
}
}
throw new IllegalArgumentException(String.format(INVALID_ERROR_CODE, code));
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 无效的授权
*
* @author BladeX
*/
public class GranterInvalidException extends OAuth2Exception {
public GranterInvalidException(String msg) {
super(ExceptionCode.INVALID_GRANTER, msg);
}
public GranterInvalidException(String msg, Throwable cause) {
super(ExceptionCode.INVALID_GRANTER, msg, cause);
}
}

View File

@@ -0,0 +1,106 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* OAuth2ErrorCodes
*
* @author BladeX
*/
public interface OAuth2ErrorCode {
/**
* 无效请求 - 请求缺少必要的参数或格式不正确。
*/
int INVALID_REQUEST = 2000;
/**
* 用户不存在 - 指定的用户ID不存在或无效。
*/
int USER_NOT_FOUND = 2001;
/**
* 用户租户不存在 - 指定的用户租户未授权。
*/
int USER_TENANT_NOT_FOUND = 2002;
/**
* 用户登录失败次数过多 - 用户登录失败次数过多。
*/
int USER_TOO_MANY_FAILS = 2003;
/**
* 用户认证失败 - 指定的用户认证信息错误或无效。
*/
int INVALID_USER = 2004;
/**
* 用户未授权 - 指定的用户未授权。
*/
int UNAUTHORIZED_USER = 2005;
/**
* 用户租户未授权 - 指定的用户租户未授权。
*/
int UNAUTHORIZED_USER_TENANT = 2006;
/**
* 令牌刷新错误或无效 - 刷新令牌认证信息错误或无效。
*/
int INVALID_REFRESH_TOKEN = 2010;
/**
* 客户端不存在 - 指定的客户端ID不存在或无效。
*/
int CLIENT_NOT_FOUND = 3000;
/**
* 客户端认证失败 - 客户端提供的认证信息错误或无效。
*/
int INVALID_CLIENT = 3001;
/**
* 回调地址错误或无效 - 客户端回调地址错误或无效。
*/
int INVALID_CLIENT_REDIRECT_URI = 3002;
/**
* 客户端未授权 - 客户端无权执行此操作。
*/
int UNAUTHORIZED_CLIENT = 3003;
/**
* 不支持的授权类型 - 请求的授权类型不被服务器支持。
*/
int UNSUPPORTED_GRANT_TYPE = 4000;
/**
* 无效的授权类型 - 提供的授权类型无效、过期或被撤销。
*/
int INVALID_GRANTER = 4001;
/**
* 无效的授权范围 - 请求的授权范围无效、未知或格式不正确。
*/
int INVALID_SCOPE = 4002;
/**
* 服务器错误 - 服务器内部错误,无法完成请求。
*/
int SERVER_ERROR = 5000;
/**
* 访问被拒绝 - 由于各种原因,服务器拒绝执行此操作。
*/
int ACCESS_DENIED = 5001;
/**
* 服务暂不可用 - 服务器暂时过载或维护,无法处理请求。
*/
int TEMPORARILY_UNAVAILABLE = 5002;
}

View File

@@ -0,0 +1,47 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* OAuth2ErrorMessage
*
* @author BladeX
*/
public interface OAuth2ErrorMessage {
String INVALID_GRANT_TYPE = "无效的授权类型: %s";
String CLIENT_AUTHORIZATION_FAILED = "客户端认证失败, 请检查请求头 [Authorization] 信息";
String CLIENT_TOKEN_PARSE_FAILED = "客户端令牌解析失败";
String INVALID_CLIENT_TOKEN = "客户端令牌不合法";
String AUTHORIZATION_NOT_FOUND = "请求头中未找到 [Authorization] 信息";
String INVALID_ERROR_CODE = "无效的错误代码: %s";
String USER_HAS_NO_TENANT = "未获得用户的租户信息";
String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
String USER_HAS_TOO_MANY_FAILS = "登录错误次数过多,请稍后再试";
}

View File

@@ -0,0 +1,75 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
import lombok.Getter;
import java.io.Serial;
/**
* OAuth2通用异常
*
* @author BladeX
*/
@Getter
public class OAuth2Exception extends RuntimeException {
@Serial
private static final long serialVersionUID = 1L;
private final ExceptionCode exceptionCode;
public OAuth2Exception(String message) {
super(message);
this.exceptionCode = ExceptionCode.ACCESS_DENIED;
}
public OAuth2Exception(ExceptionCode exceptionCode) {
super(exceptionCode.getMessage());
this.exceptionCode = exceptionCode;
}
public OAuth2Exception(ExceptionCode exceptionCode, String message) {
super(message);
this.exceptionCode = exceptionCode;
}
public OAuth2Exception(ExceptionCode exceptionCode, Throwable cause) {
super(cause);
this.exceptionCode = exceptionCode;
}
public OAuth2Exception(ExceptionCode exceptionCode, String message, Throwable cause) {
super(message, cause);
this.exceptionCode = exceptionCode;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
import io.jsonwebtoken.JwtException;
import org.springblade.core.oauth2.endpoint.OAuth2AuthorizationEndpoint;
import org.springblade.core.oauth2.endpoint.OAuth2SocialEndpoint;
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
import org.springblade.core.oauth2.provider.OAuth2Response;
import org.springblade.core.secure.exception.SecureException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* OAuth2ExceptionHandler
*
* @author BladeX
*/
@ControllerAdvice(basePackageClasses = {OAuth2AuthorizationEndpoint.class, OAuth2SocialEndpoint.class, OAuth2TokenEndPoint.class})
public class OAuth2ExceptionHandler {
@ExceptionHandler(OAuth2Exception.class)
public ResponseEntity<?> handleOAuth2Exception(OAuth2Exception ex) {
// 统一处理验证失败的情况
return ResponseEntity.ok(OAuth2Response.create().of(Boolean.FALSE, ex.getExceptionCode().getCode(), ex.getMessage()));
}
@ExceptionHandler(SecureException.class)
public ResponseEntity<?> handleSecureException(SecureException ex) {
// 统一处理验证失败的情况
return ResponseEntity.ok(OAuth2Response.create().of(Boolean.FALSE, OAuth2ErrorCode.INVALID_USER, ex.getMessage()));
}
@ExceptionHandler(JwtException.class)
public ResponseEntity<?> handleJwtException(JwtException ex) {
// 统一处理验证失败的情况
return ResponseEntity.ok(OAuth2Response.create().of(Boolean.FALSE, OAuth2ErrorCode.ACCESS_DENIED, ex.getMessage()));
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 用户认证失败
*
* @author BladeX
*/
public class UserInvalidException extends OAuth2Exception {
public UserInvalidException(String msg) {
super(ExceptionCode.INVALID_USER, msg);
}
public UserInvalidException(String msg, Throwable cause) {
super(ExceptionCode.INVALID_USER, msg, cause);
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 用户未授权
*
* @author BladeX
*/
public class UserUnauthorizedException extends OAuth2Exception {
public UserUnauthorizedException(String msg) {
super(ExceptionCode.UNAUTHORIZED_USER, msg);
}
public UserUnauthorizedException(String msg, Throwable cause) {
super(ExceptionCode.UNAUTHORIZED_USER, msg, cause);
}
}

View File

@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.exception;
/**
* 用户名未找到
*
* @author BladeX
*/
public class UsernameNotFoundException extends OAuth2Exception {
public UsernameNotFoundException(String msg) {
super(ExceptionCode.USER_NOT_FOUND, msg);
}
public UsernameNotFoundException(String msg, Throwable cause) {
super(ExceptionCode.USER_NOT_FOUND, msg, cause);
}
}

View File

@@ -0,0 +1,148 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import lombok.RequiredArgsConstructor;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.exception.OAuth2ErrorCode;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
import org.springblade.core.oauth2.utils.OAuth2Util;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
/**
* AbstractTokenGranter
*
* @author BladeX
*/
@RequiredArgsConstructor
public abstract class AbstractTokenGranter implements TokenGranter {
private final OAuth2ClientService clientService;
private final OAuth2UserService userService;
private final PasswordHandler passwordHandler;
protected TokenGranterEnhancer enhancer;
public abstract String type();
@Override
public void enhancer(TokenGranterEnhancer enhancer) {
this.enhancer = enhancer;
}
@Override
public OAuth2Client client(OAuth2Request request) {
// 解析请求头
String[] tokens = request.getClientFromAuthorization();
// 获取clientId
String clientId = tokens[0];
// 获取clientSecret
String clientSecret = tokens[1];
// 获取客户端信息
OAuth2Client client = clientService.loadByClientId(clientId);
// 校验客户端信息
if (!clientService.validateClient(client, clientId, clientSecret)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_CLIENT);
}
// 校验授权类型
if (!clientService.validateGranter(client, type())) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_GRANTER);
}
// 返回客户端信息
return client;
}
@Override
public OAuth2User user(OAuth2Request request) {
// 获取用户信息
OAuth2User user = (StringUtil.isNotBlank(request.getUserId()) && request.isRefreshToken())
? userService.loadByUserId(request.getUserId(), request)
: userService.loadByUsername(request.getUsername(), request);
// 校验用户信息
if (!userService.validateUser(user)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
}
// 校验用户密码
if ((request.isCaptchaCode() || request.isPassword()) && !passwordHandler.matches(request.getPassword(), user.getPassword())) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
}
// 设置客户端信息
user.setClient(client(request));
// 返回用户信息
return user;
}
@Override
public OAuth2Token token(OAuth2User user, OAuth2Request request) {
TokenInfo accessToken = OAuth2Util.createAccessToken(user);
TokenInfo refreshToken = OAuth2Util.createRefreshToken(user);
OAuth2Token token = OAuth2Token.create();
token.getArgs().set(TokenConstant.TENANT_ID, user.getTenantId())
.set(TokenConstant.USER_ID, user.getUserId())
.set(TokenConstant.DEPT_ID, user.getDeptId())
.set(TokenConstant.POST_ID, user.getPostId())
.set(TokenConstant.ROLE_ID, user.getRoleId())
.set(TokenConstant.OAUTH_ID, user.getOauthId())
.set(TokenConstant.ACCOUNT, user.getAccount())
.set(TokenConstant.USER_NAME, user.getAccount())
.set(TokenConstant.NICK_NAME, user.getName())
.set(TokenConstant.REAL_NAME, user.getRealName())
.set(TokenConstant.ROLE_NAME, Func.join(user.getAuthorities()))
.set(TokenConstant.AVATAR, Func.toStr(user.getAvatar(), TokenConstant.DEFAULT_AVATAR))
.set(TokenConstant.ACCESS_TOKEN, accessToken.getToken())
.set(TokenConstant.REFRESH_TOKEN, refreshToken.getToken())
.set(TokenConstant.TOKEN_TYPE, TokenConstant.BEARER)
.set(TokenConstant.EXPIRES_IN, accessToken.getExpire())
.set(TokenConstant.DETAIL, user.getDetail())
.set(TokenConstant.LICENSE, TokenConstant.LICENSE_NAME);
return token.setAccessToken(accessToken.getToken())
.setAccessTokenExpire(accessToken.getExpire())
.setRefreshToken(refreshToken.getToken())
.setRefreshTokenExpire(refreshToken.getExpire());
}
}

View File

@@ -0,0 +1,100 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.oauth2.exception.ExceptionCode;
import org.springblade.core.oauth2.exception.OAuth2ErrorCode;
import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.utils.OAuth2CodeUtil;
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* AuthorizationCodeGranter
*
* @author BladeX
*/
@Component
public class AuthorizationCodeGranter extends AbstractTokenGranter {
private final OAuth2UserService userService;
private final PasswordHandler passwordHandler;
private final BladeRedis bladeRedis;
public AuthorizationCodeGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, BladeRedis bladeRedis) {
super(clientService, userService, passwordHandler);
this.userService = userService;
this.passwordHandler = passwordHandler;
this.bladeRedis = bladeRedis;
}
@Override
public String type() {
return AUTHORIZATION_CODE;
}
@Override
public OAuth2User user(OAuth2Request request) {
// 获取客户端信息并校验
OAuth2Client client = client(request);
if (!StringUtil.equals(client.getWebServerRedirectUri(), request.getRedirectUri())) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_CLIENT_REDIRECT_URI);
}
// 根据code获取用户信息
String code = request.getCode();
OAuth2User user = bladeRedis.get(OAuth2CodeUtil.codeKey(code));
// 判断用户是否存在
if (ObjectUtil.isNotEmpty(user)) {
// 校验用户信息
if (!userService.validateUser(user)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
}
// 校验用户密码
if ((request.isCaptchaCode() || request.isPassword()) && !passwordHandler.matches(request.getPassword(), user.getPassword())) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
}
// 设置客户端信息
user.setClient(client);
// 返回user
return Optional.ofNullable(this.enhancer)
.map(enhancer -> enhancer.enhance(user, request))
.orElse(user);
}
throw new UserInvalidException(ExceptionCode.INVALID_USER.getMessage());
}
}

View File

@@ -0,0 +1,90 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.service.impl.OAuth2UserDetail;
import org.springblade.core.oauth2.utils.OAuth2Util;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.tool.utils.Func;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* ClientCredentialsGranter
*
* @author BladeX
*/
@Component
public class ClientCredentialsGranter extends AbstractTokenGranter {
public ClientCredentialsGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler) {
super(clientService, userService, passwordHandler);
}
@Override
public String type() {
return CLIENT_CREDENTIALS;
}
@Override
public OAuth2User user(OAuth2Request request) {
OAuth2UserDetail user = new OAuth2UserDetail();
OAuth2Client client = client(request);
user.setClient(client);
user.setAccount(client.getClientId());
user.setName(client.getClientId());
return Optional.ofNullable(this.enhancer)
.map(enhancer -> enhancer.enhance(user, request))
.orElse(user);
}
@Override
public OAuth2Token token(OAuth2User user, OAuth2Request request) {
TokenInfo accessToken = OAuth2Util.createClientAccessToken(user.getClient());
OAuth2Token token = OAuth2Token.create();
token.getArgs().set(TokenConstant.CLIENT_ID, user.getClient().getClientId())
.set(TokenConstant.AVATAR, Func.toStr(user.getAvatar(), TokenConstant.DEFAULT_AVATAR))
.set(TokenConstant.ACCESS_TOKEN, accessToken.getToken())
.set(TokenConstant.TOKEN_TYPE, TokenConstant.BEARER)
.set(TokenConstant.EXPIRES_IN, accessToken.getExpire())
.set(TokenConstant.DETAIL, user.getDetail())
.set(TokenConstant.LICENSE, TokenConstant.LICENSE_NAME);
return token.setAccessToken(accessToken.getToken()).setAccessTokenExpire(accessToken.getExpire());
}
}

View File

@@ -0,0 +1,94 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.service.impl.OAuth2ClientDetail;
import org.springblade.core.oauth2.service.impl.OAuth2UserDetail;
import org.springblade.core.oauth2.utils.OAuth2Util;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.tool.utils.Func;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* ImplicitGranter
*
* @author BladeX
*/
@Component
public class ImplicitGranter extends AbstractTokenGranter {
public ImplicitGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler) {
super(clientService, userService, passwordHandler);
}
@Override
public String type() {
return IMPLICIT;
}
@Override
public OAuth2Client client(OAuth2Request request) {
return new OAuth2ClientDetail();
}
@Override
public OAuth2User user(OAuth2Request request) {
OAuth2UserDetail user = new OAuth2UserDetail();
user.setAccount(request.getUsername());
user.setName(request.getUsername());
return Optional.ofNullable(this.enhancer)
.map(enhancer -> enhancer.enhance(user, request))
.orElse(user);
}
@Override
public OAuth2Token token(OAuth2User user, OAuth2Request request) {
TokenInfo accessToken = OAuth2Util.createImplicitAccessToken(user);
OAuth2Token token = OAuth2Token.create();
token.getArgs().set(TokenConstant.USER_NAME, user.getAccount())
.set(TokenConstant.AVATAR, Func.toStr(user.getAvatar(), TokenConstant.DEFAULT_AVATAR))
.set(TokenConstant.ACCESS_TOKEN, accessToken.getToken())
.set(TokenConstant.TOKEN_TYPE, TokenConstant.BEARER)
.set(TokenConstant.EXPIRES_IN, accessToken.getExpire())
.set(TokenConstant.DETAIL, user.getDetail())
.set(TokenConstant.LICENSE, TokenConstant.LICENSE_NAME);
return token.setAccessToken(accessToken.getToken()).setAccessTokenExpire(accessToken.getExpire());
}
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* PasswordTokenGranter
*
* @author BladeX
*/
@Component
public class PasswordTokenGranter extends AbstractTokenGranter {
public PasswordTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler) {
super(clientService, userService, passwordHandler);
}
@Override
public String type() {
return PASSWORD;
}
@Override
public OAuth2User user(OAuth2Request request) {
OAuth2User user = super.user(request);
return Optional.ofNullable(this.enhancer)
.map(enhancer -> enhancer.enhance(user, request))
.orElse(user);
}
}

View File

@@ -0,0 +1,118 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import io.jsonwebtoken.Claims;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.exception.OAuth2ErrorCode;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
/**
* PasswordTokenGranter
*
* @author BladeX
*/
@Component
public class RefreshTokenGranter extends PasswordTokenGranter {
private final JwtProperties jwtProperties;
public RefreshTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, JwtProperties jwtProperties) {
super(clientService, userService, passwordHandler);
this.jwtProperties = jwtProperties;
}
@Override
public String type() {
return REFRESH_TOKEN;
}
@Override
public OAuth2User user(OAuth2Request request) {
String refreshToken = request.getRefreshToken();
Claims refreshClaims = Objects.requireNonNull(JwtUtil.parseJWT(refreshToken));
// 校验refreshToken的合法性
if (!judgeRefreshToken(refreshClaims, refreshToken)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_REFRESH_TOKEN);
}
// 校验refreshToken的格式
String tokenType = String.valueOf(refreshClaims.get(TokenConstant.TOKEN_TYPE));
if (!StringUtil.equals(tokenType, TokenConstant.REFRESH_TOKEN)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_REFRESH_TOKEN);
}
// 校验refreshToken是否可获取username
String userId = String.valueOf(refreshClaims.get(TokenConstant.USER_ID));
if (StringUtil.isBlank(userId)) {
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.USER_NOT_FOUND);
}
// 设置username
request.setUserId(userId);
return super.user(request);
}
/**
* 校验refreshToken的合法性
*
* @param refreshToken 待校验的refreshToken
* @return refreshToken是否合法
*/
private boolean judgeRefreshToken(Claims refreshClaims, String refreshToken) {
// 首先检查JWT是否启用单人登录模式如果不是则直接返回true
if (!jwtProperties.getState() || !jwtProperties.getSingle()) {
return true;
}
// 解析JWT如果无法解析则认为不合法
return Optional.ofNullable(refreshClaims)
.map(claims -> {
// 从JWT claims中提取tenantId, clientId, 和 userId
String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
String clientId = String.valueOf(claims.get(TokenConstant.CLIENT_ID));
String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
// 根据提取的信息和refreshToken生成新的token
String token = JwtUtil.getRefreshToken(tenantId, clientId, userId, refreshToken);
// 比较新生成的token与传入的refreshToken是否一致如果一致则认为合法
return StringUtil.equalsIgnoreCase(token, refreshToken);
})
.orElse(false); // 如果claims为空则返回false
}
}

View File

@@ -0,0 +1,80 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.oauth2.constant.OAuth2GranterConstant;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* 授权认证统一接口.
*
* @author BladeX
*/
public interface TokenGranter extends OAuth2GranterConstant {
/**
* 获取授权模式
*
* @return String
*/
String type();
/**
* 获取客户端信息
*
* @param request 授权参数
* @return OAuth2Client
*/
OAuth2Client client(OAuth2Request request);
/**
* 获取用户信息
*
* @param request 授权参数
* @return OAuth2User
*/
OAuth2User user(OAuth2Request request);
/**
* 创建令牌
*
* @param user 用户信息
* @param request 授权参数
* @return OAuth2Token
*/
OAuth2Token token(OAuth2User user, OAuth2Request request);
/**
* 自定义增强
*
* @param enhancer enhancer
*/
void enhancer(TokenGranterEnhancer enhancer);
}

View File

@@ -0,0 +1,55 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import org.springblade.core.oauth2.constant.OAuth2GranterConstant;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* TokenGranterEnhancer
*
* @author BladeX
*/
public interface TokenGranterEnhancer extends OAuth2GranterConstant {
/**
* 获取授权模式
*
* @return String
*/
String type();
/**
* 增强用户令牌
*
* @param user 用户信息
* @param request 授权参数
* @return OAuth2User
*/
OAuth2User enhance(OAuth2User user, OAuth2Request request);
}

View File

@@ -0,0 +1,120 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.granter;
import lombok.AllArgsConstructor;
import org.springblade.core.oauth2.exception.GranterInvalidException;
import org.springblade.core.oauth2.props.OAuth2Properties;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.springblade.core.oauth2.constant.OAuth2GranterConstant.*;
import static org.springblade.core.oauth2.exception.OAuth2ErrorMessage.INVALID_GRANT_TYPE;
/**
* TokenGranterFactory
*
* @author BladeX
*/
@AllArgsConstructor
public class TokenGranterFactory {
/**
* TokenGranter 集合
*/
private final List<TokenGranter> tokenGranters;
/**
* TokenGranterEnhancer 集合
*/
private final List<TokenGranterEnhancer> tokenGranterEnhancers;
/**
* OAuth2 属性配置
*/
private final OAuth2Properties properties;
/**
* TokenGranter 缓存池,使用 ConcurrentHashMap 保证线程安全。
*/
private static final Map<String, TokenGranter> GRANTER_POOL = new ConcurrentHashMap<>();
/**
* 根据授权模式获取 TokenGranter如果缓存中不存在则尝试创建。
*
* @param grantType 授权模式
* @return TokenGranter 实例
* @throws IllegalArgumentException 如果请求的授权类型不支持
*/
public TokenGranter create(String grantType) {
// 使用 computeIfAbsent 实现延迟加载
return GRANTER_POOL.computeIfAbsent(grantType, this::initializeTokenGranter);
}
/**
* 尝试根据授权类型初始化 TokenGranter。
*
* @param grantType 授权模式
* @return 初始化的 TokenGranter
* @throws GranterInvalidException 如果无法识别授权类型
*/
private TokenGranter initializeTokenGranter(String grantType) {
// 根据授权类型查找对应的 TokenGranter 并应用第一个找到的增强类
return tokenGranters.stream()
.filter(granter -> granter.type().equals(grantType) && isGranterEnabled(granter))
.peek(granter -> tokenGranterEnhancers.stream()
.filter(enhancer -> enhancer.type().equals(grantType))
.findFirst()
.ifPresent(granter::enhancer))
.findFirst()
.orElseThrow(() -> new GranterInvalidException(String.format(INVALID_GRANT_TYPE, grantType)));
}
/**
* 判断 TokenGranter 是否启用。
*
* @param granter TokenGranter 实例
* @return 是否启用
*/
private boolean isGranterEnabled(TokenGranter granter) {
return switch (granter.type()) {
case AUTHORIZATION_CODE -> properties.getGranter().getAuthorizationCode();
case PASSWORD -> properties.getGranter().getPassword();
case REFRESH_TOKEN -> properties.getGranter().getRefreshToken();
case CLIENT_CREDENTIALS -> properties.getGranter().getClientCredentials();
case IMPLICIT -> properties.getGranter().getImplicit();
case CAPTCHA -> properties.getGranter().getCaptcha();
case SMS_CODE -> properties.getGranter().getSmsCode();
case WECHAT_APPLET -> properties.getGranter().getWechatApplet();
case SOCIAL -> properties.getGranter().getSocial();
case REGISTER -> properties.getGranter().getRegister();
default -> true;
};
}
}

View File

@@ -0,0 +1,91 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import org.springblade.core.oauth2.exception.ExceptionCode;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Validation;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.tool.utils.Func;
/**
* AbstractAuthorizationHandler
*
* @author BladeX
*/
public abstract class AbstractAuthorizationHandler implements AuthorizationHandler {
/**
* 认证校验
*
* @param user 用户信息
* @param request 请求信息
* @return boolean
*/
@Override
public OAuth2Validation authValidation(OAuth2User user, OAuth2Request request) {
if (request.isClientCredentials() || request.isImplicit() || request.isSocial()) {
return new OAuth2Validation();
}
if (Func.hasEmpty(user, user.getUserId())) {
return buildValidationFailure(ExceptionCode.USER_NOT_FOUND);
}
if (Func.isEmpty(user.getAuthorities())) {
return buildValidationFailure(ExceptionCode.UNAUTHORIZED_USER);
}
return new OAuth2Validation();
}
/**
* 认证成功回调
*
* @param user 用户信息
*/
@Override
public abstract void authSuccessful(OAuth2User user, OAuth2Request request);
/**
* 认证失败回调
*
* @param user 用户信息
* @param validation 失败信息
*/
@Override
public abstract void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation);
/**
* 构建认证失败返回
*
* @param errorCode 错误码
* @return 认证结果
*/
public OAuth2Validation buildValidationFailure(ExceptionCode errorCode) {
return new OAuth2Validation().setSuccess(false)
.setCode(errorCode.getCode())
.setMessage(errorCode.getMessage());
}
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Validation;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* OAuth2AuthorizationHandler
*
* @author BladeX
*/
public interface AuthorizationHandler {
/**
* 认证校验
*
* @param user 用户信息
* @param request 请求信息
* @return boolean
*/
OAuth2Validation authValidation(OAuth2User user, OAuth2Request request);
/**
* 认证成功回调
*
* @param user 用户信息
*/
void authSuccessful(OAuth2User user, OAuth2Request request);
/**
* 认证失败回调
*
* @param user 用户信息
* @param validation 失败信息
*/
void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation);
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Validation;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* AbstractAuthorizationHandler
*
* @author BladeX
*/
@Slf4j
public class OAuth2AuthorizationHandler extends AbstractAuthorizationHandler {
/**
* 认证成功回调
*
* @param user 用户信息
*/
@Override
public void authSuccessful(OAuth2User user, OAuth2Request request) {
}
/**
* 认证失败回调
*
* @param user 用户信息
* @param validation 失败信息
*/
@Override
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
log.error("用户:{},认证失败,失败原因:{}", user.getAccount(), validation.getMessage());
}
}

View File

@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import org.springblade.core.tool.utils.DigestUtil;
/**
* BladePasswordHandler
*
* @author BladeX
*/
public class OAuth2PasswordHandler implements PasswordHandler {
/**
* 判断密码是否匹配
*
* @param rawPassword 请求时提交的原密码
* @param encodedPassword 数据库加密后的密码
* @return boolean
*/
@Override
public boolean matches(String rawPassword, String encodedPassword) {
return encodedPassword.equals(encode(rawPassword));
}
/**
* 加密密码规则
*
* @param rawPassword 密码
* @return 加密后的密码
*/
@Override
public String encode(String rawPassword) {
return DigestUtil.hex(rawPassword);
}
}

View File

@@ -0,0 +1,80 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import lombok.RequiredArgsConstructor;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* BladeTokenHandler
*
* @author BladeX
*/
@RequiredArgsConstructor
public class OAuth2TokenHandler implements TokenHandler {
private final JwtProperties properties;
/**
* 令牌增强
*
* @param user 用户信息
* @param token 令牌信息
* @param request 授权参数
* @return OAuth2Token
*/
@Override
public OAuth2Token enhance(OAuth2User user, OAuth2Token token, OAuth2Request request) {
//令牌状态配置, 仅在生成AccessToken时候执行
if (properties.getState() && token.hasAccessToken()) {
JwtUtil.addAccessToken(
user.getTenantId(),
user.getClient().getClientId(),
user.getUserId(),
token.getAccessToken(),
token.getAccessTokenExpire()
);
}
//令牌状态配置, 仅在生成RefreshToken时候执行
if (properties.getState() && properties.getSingle() && token.hasRefreshToken()) {
JwtUtil.addRefreshToken(
user.getTenantId(),
user.getClient().getClientId(),
user.getUserId(),
token.getRefreshToken(),
token.getRefreshTokenExpire()
);
}
// 返回令牌
return token;
}
}

View File

@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
/**
* PasswordHandler
*
* @author BladeX
*/
public interface PasswordHandler {
/**
* 判断密码是否匹配
*
* @param rawPassword 请求时提交的原密码
* @param encodedPassword 数据库加密后的密码
* @return boolean
*/
boolean matches(String rawPassword, String encodedPassword);
/**
* 加密密码规则
*
* @param rawPassword 密码
* @return 加密后的密码
*/
String encode(String rawPassword);
}

View File

@@ -0,0 +1,48 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.handler;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.provider.OAuth2Token;
import org.springblade.core.oauth2.service.OAuth2User;
/**
* TokenHandler
*
* @author BladeX
*/
public interface TokenHandler {
/**
* 令牌增强
*
* @param user 用户信息
* @param token 令牌信息
* @param request 授权参数
* @return OAuth2Token
*/
OAuth2Token enhance(OAuth2User user, OAuth2Token token, OAuth2Request request);
}

View File

@@ -0,0 +1,108 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.props;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* OAuth2Property
*
* @author BladeX
*/
@Getter
@Setter
@ConfigurationProperties(OAuth2Properties.PREFIX)
public class OAuth2Properties {
/**
* 配置前缀
*/
public static final String PREFIX = "blade.oauth2";
/**
* 是否开启OAuth2
*/
private Boolean enabled = true;
/**
* code缓存时间
*/
private long codeTimeout = 10 * 60L;
/**
* 授权模式
*/
private Granter granter = new Granter();
@Data
@NoArgsConstructor
public static class Granter {
/**
* 是否开启授权码模式
*/
private Boolean authorizationCode = true;
/**
* 是否开启验证码模式
*/
private Boolean captcha = true;
/**
* 是否开启密码模式
*/
private Boolean password = true;
/**
* 是否开启刷新token模式
*/
private Boolean refreshToken = true;
/**
* 是否开启客户端模式
*/
private Boolean clientCredentials = true;
/**
* 是否开启简化模式
*/
private Boolean implicit = true;
/**
* 是否手机验证码模式
*/
private Boolean smsCode = true;
/**
* 是否开启微信小程序模式
*/
private Boolean wechatApplet = true;
/**
* 是否开启开放平台模式
*/
private Boolean social = true;
/**
* 是否开启注册模式
*/
private Boolean register = true;
}
}

View File

@@ -0,0 +1,107 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.provider;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.springblade.core.oauth2.constant.OAuth2ParameterConstant.*;
/**
* OAuth2AuthorizationRequest
*
* @author BladeX
*/
@Data
public class OAuth2AuthorizationRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String responseType;
private String tenantId;
private String clientId;
private String redirectUri;
private String scope;
private String state;
/**
* 实例化
*/
public static OAuth2AuthorizationRequest create() {
return new OAuth2AuthorizationRequest();
}
/**
* 构建参数
*
* @return OAuth2AuthorizationRequest
*/
public OAuth2AuthorizationRequest buildParameters() {
HttpServletRequest request = Objects.requireNonNull(WebUtil.getRequest());
this.responseType = request.getParameter(RESPONSE_TYPE);
this.tenantId = request.getParameter(TENANT_ID);
this.clientId = request.getParameter(CLIENT_ID);
this.redirectUri = request.getParameter(REDIRECT_URI);
this.scope = request.getParameter(SCOPE);
this.state = request.getParameter(STATE);
return this;
}
/**
* 获取参数
*
* @return Map
*/
public Map<String, String> getParameters() {
Map<String, String> parameters = new HashMap<>();
parameters.put(RESPONSE_TYPE, this.responseType);
parameters.put(CLIENT_ID, this.clientId);
parameters.put(REDIRECT_URI, this.redirectUri);
if (scope != null) {
parameters.put(SCOPE, this.scope);
}
if (this.tenantId != null) {
parameters.put(STATE, this.tenantId);
}
if (state != null) {
parameters.put(STATE, this.state);
}
return parameters;
}
public String getState() {
return StringUtil.isBlank(this.state) ? this.tenantId : this.state;
}
}

View File

@@ -0,0 +1,386 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.provider;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import org.springblade.core.oauth2.utils.OAuth2Util;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import static org.springblade.core.oauth2.constant.OAuth2GranterConstant.PASSWORD;
import static org.springblade.core.oauth2.constant.OAuth2GranterConstant.REFRESH_TOKEN;
import static org.springblade.core.oauth2.constant.OAuth2GranterConstant.*;
import static org.springblade.core.oauth2.constant.OAuth2ParameterConstant.*;
import static org.springblade.core.oauth2.constant.OAuth2TokenConstant.*;
/**
* OAuth2参数类
*
* @author BladeX
*/
@Data
public class OAuth2Request {
/**
* 实例化
*/
public static OAuth2Request create() {
return new OAuth2Request();
}
/**
* 请求参数
*/
private Kv parameterArgs = Kv.create();
/**
* 头部参数
*/
private Kv headerArgs = Kv.create();
/**
* 自动构建参数
*
* @return OAuth2Request
*/
public OAuth2Request buildArgs() {
return this.buildParameterArgs().buildHeaderArgs();
}
/**
* 自动构建请求头参数
*
* @return OAuth2Request
*/
public OAuth2Request buildParameterArgs() {
HttpServletRequest request = Objects.requireNonNull(WebUtil.getRequest());
Arrays.stream(new String[]{
CLIENT_ID, CLIENT_SECRET, ACCESS_TOKEN, REFRESH_TOKEN, TENANT_ID, USERNAME, PASSWORD, NAME, PHONE, EMAIL, GRANT_TYPE, SCOPE, REDIRECT_URI, RESPONSE_TYPE, CODE, STATE, SOURCE
}).forEach(param -> Optional.ofNullable(request.getParameter(param)).ifPresent(value -> parameterArgs.set(param, value)));
return this;
}
/**
* 自动构建头部参数
*
* @return OAuth2Request
*/
public OAuth2Request buildHeaderArgs() {
HttpServletRequest request = Objects.requireNonNull(WebUtil.getRequest());
Arrays.stream(new String[]{
HEADER_AUTHORIZATION, TENANT_HEADER, USER_HEADER, ROLE_HEADER, DEPT_HEADER, USER_TYPE_HEADER, CAPTCHA_HEADER_KEY, CAPTCHA_HEADER_CODE
}).forEach(param -> Optional.ofNullable(request.getHeader(param)).ifPresent(value -> headerArgs.set(param, value)));
return this;
}
/**
* 获取客户端ID
*
* @return String
*/
public String getClientIdFromParameter() {
return parameterArgs.getStr(CLIENT_ID);
}
/**
* 获取客户端密钥
*
* @return String
*/
public String getClientSecretFromParameter() {
return parameterArgs.getStr(CLIENT_SECRET);
}
/**
* 获取客户端ID和密钥
*
* @return String[]
*/
public String[] getClientFromAuthorization() {
return OAuth2Util.extractAndDecodeAuthorization();
}
/**
* 获取令牌
*
* @return String
*/
public String getToken() {
return headerArgs.getStr(TOKEN_HEADER);
}
/**
* 获取租户编号
*
* @return String
*/
public String getTenantId() {
if (StringUtil.isBlank(headerArgs.getStr(TENANT_HEADER))) {
return parameterArgs.getStr(TENANT_ID);
}
return headerArgs.getStr(TENANT_HEADER);
}
/**
* 获取用户ID
*
* @return String
*/
public String getUserId() {
return headerArgs.getStr(USER_HEADER);
}
/**
* 获取用户名
*
* @return String
*/
public String getUsername() {
return parameterArgs.getStr(USERNAME);
}
/**
* 获取密码
*
* @return String
*/
public String getPassword() {
return parameterArgs.getStr(PASSWORD);
}
/**
* 获取用户名字
*
* @return String
*/
public String getName() {
return parameterArgs.getStr(NAME);
}
/**
* 获取手机号
*
* @return String
*/
public String getPhone() {
return parameterArgs.getStr(PHONE);
}
/**
* 获取电子游戏
*
* @return String
*/
public String getEmail() {
return parameterArgs.getStr(EMAIL);
}
/**
* 获取用户名
*
* @return String
*/
public String getUserType() {
return headerArgs.getStr(USER_TYPE_HEADER);
}
/**
* 获取用户部门
*
* @return String
*/
public String getUserDept() {
return headerArgs.getStr(DEPT_HEADER);
}
/**
* 获取用户角色
*
* @return String
*/
public String getUserRole() {
return headerArgs.getStr(ROLE_HEADER);
}
/**
* 获取验证码key
*/
public String getCaptchaKey() {
return headerArgs.getStr(CAPTCHA_HEADER_KEY);
}
/**
* 获取验证码code
*/
public String getCaptchaCode() {
return headerArgs.getStr(CAPTCHA_HEADER_CODE);
}
/**
* 获取授权类型
*
* @return String
*/
public String getGrantType() {
return parameterArgs.getStr(GRANT_TYPE);
}
/**
* 获取刷新令牌
*
* @return String
*/
public String getRefreshToken() {
return parameterArgs.getStr(REFRESH_TOKEN);
}
/**
* 获取验证code
*
* @return String
*/
public String getCode() {
return parameterArgs.getStr(CODE);
}
/**
* 获取状态
*
* @return String
*/
public String getState() {
return parameterArgs.getStr(STATE);
}
/**
* 获取来源
*
* @return String
*/
public String getSource() {
return parameterArgs.getStr(SOURCE);
}
/**
* 获取回调地址
*
* @return String
*/
public String getRedirectUri() {
return parameterArgs.getStr(REDIRECT_URI);
}
/**
* 是否密码模式
*
* @return Boolean
*/
public Boolean isPassword() {
return PASSWORD.equals(getGrantType());
}
/**
* 是否刷新模式
*
* @return Boolean
*/
public Boolean isRefreshToken() {
return REFRESH_TOKEN.equals(getGrantType());
}
/**
* 是否验证码模式
*
* @return Boolean
*/
public Boolean isCaptchaCode() {
return CAPTCHA.equals(getGrantType());
}
/**
* 是否密码模式
*
* @return Boolean
*/
public Boolean isClientCredentials() {
return CLIENT_CREDENTIALS.equals(getGrantType());
}
/**
* 是否简化模式
*
* @return Boolean
*/
public Boolean isImplicit() {
return IMPLICIT.equals(getGrantType());
}
/**
* 是否开放平台模式
*
* @return Boolean
*/
public Boolean isSocial() {
return SOCIAL.equals(getGrantType());
}
/**
* 设置租户ID
*
* @param tenantId 户ID
*/
public void setTenantId(String tenantId) {
this.headerArgs.set(TENANT_HEADER, tenantId);
}
/**
* 设置用户ID
*
* @param userId 用户ID
*/
public void setUserId(String userId) {
this.headerArgs.set(USER_HEADER, userId);
}
/**
* 设置用户名
*
* @param username 用户名
*/
public void setUsername(String username) {
this.parameterArgs.set(USERNAME, username);
}
}

View File

@@ -0,0 +1,73 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.provider;
import lombok.Data;
import org.springblade.core.tool.support.Kv;
import java.io.Serial;
import java.io.Serializable;
import static org.springblade.core.oauth2.constant.OAuth2ResponseConstant.*;
/**
* OAuth2Response
*
* @author BladeX
*/
@Data
public class OAuth2Response implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 实例化
*/
public static OAuth2Response create() {
return new OAuth2Response();
}
/**
* 响应参数
*/
private Kv args = Kv.create();
public Kv of(boolean success, int errorCode, String errorDescription) {
args.set(SUCCESS, success);
args.set(ERROR_CODE, errorCode);
args.set(ERROR_DESCRIPTION, errorDescription);
return args;
}
public Kv ofValidation(OAuth2Validation validation) {
args.set(SUCCESS, validation.isSuccess());
args.set(ERROR_CODE, validation.getCode());
args.set(ERROR_DESCRIPTION, validation.getMessage());
return args;
}
}

View File

@@ -0,0 +1,99 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.provider;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.StringUtil;
import java.io.Serial;
import java.io.Serializable;
/**
* OAuth2Token
*
* @author BladeX
*/
@Data
@Accessors(chain = true)
public class OAuth2Token implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 实例化
*/
public static OAuth2Token create() {
return new OAuth2Token();
}
/**
* 令牌值
*/
private String accessToken;
/**
* 刷新令牌值
*/
private String refreshToken;
/**
* 令牌过期秒数
*/
private int accessTokenExpire;
/**
* 刷新令牌过期秒数
*/
private int refreshTokenExpire;
/**
* 令牌参数
*/
private Kv args = Kv.create();
/**
* 是否包含令牌
*
* @return Boolean
*/
public Boolean hasAccessToken() {
return StringUtil.isNotBlank(accessToken);
}
/**
* 是否包含刷新令牌
*
* @return Boolean
*/
public Boolean hasRefreshToken() {
return StringUtil.isNotBlank(refreshToken);
}
}

View File

@@ -0,0 +1,55 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.provider;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* OAuth2Validation
*
* @author BladeX
*/
@Data
@Accessors(chain = true)
public class OAuth2Validation {
/**
* 是否成功
*/
boolean success = true;
/**
* 状态码
*/
int code = 1000;
/**
* 验证信息
*/
String message = "认证通过";
}

View File

@@ -0,0 +1,113 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service;
import java.io.Serializable;
/**
* 多终端详情接口
*
* @author BladeX
*/
public interface OAuth2Client extends Serializable {
/**
* 获取客户端ID.
*
* @return String
*/
String getClientId();
/**
* 获取客户端密钥.
*
* @return String
*/
String getClientSecret();
/**
* 获取资源集合.
*
* @return String
*/
String getResourceIds();
/**
* 获取授权范围.
*
* @return String
*/
String getScope();
/**
* 获取授权类型.
*
* @return String
*/
String getAuthorizedGrantTypes();
/**
* 获取回调地址.
*
* @return String
*/
String getWebServerRedirectUri();
/**
* 获取权限.
*
* @return String
*/
String getAuthorities();
/**
* 获取访问令牌有效期.
*
* @return Integer
*/
Integer getAccessTokenValidity();
/**
* 获取刷新令牌有效期.
*
* @return Integer
*/
Integer getRefreshTokenValidity();
/**
* 获取附加信息.
*
* @return String
*/
String getAdditionalInformation();
/**
* 获取自动授权.
*
* @return String
*/
String getAutoapprove();
}

View File

@@ -0,0 +1,82 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service;
import org.springblade.core.oauth2.provider.OAuth2Request;
/**
* 多终端注册接口
*
* @author BladeX
*/
public interface OAuth2ClientService {
/**
* 根据clientId获取Client详情
*
* @param clientId 客户端id
* @return 客户端信息
*/
OAuth2Client loadByClientId(String clientId);
/**
* 根据clientId获取Client详情
*
* @param clientId 客户端id
* @param request 授权参数
* @return 客户端信息
*/
OAuth2Client loadByClientId(String clientId, OAuth2Request request);
/**
* 验证Client信息
*
* @param client client信息
* @param clientId 客户端id
* @param clientSecret 客户端密钥
* @return boolean
*/
boolean validateClient(OAuth2Client client, String clientId, String clientSecret);
/**
* 验证Client信息
*
* @param client client信息
* @param redirectUri 回调地址
* @return boolean
*/
boolean validateRedirectUri(OAuth2Client client, String redirectUri);
/**
* 验证授权类型
*
* @param client client信息
* @param grantType 授权类型
* @return boolean
*/
boolean validateGranter(OAuth2Client client, String grantType);
}

View File

@@ -0,0 +1,170 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service;
import org.springblade.core.tool.support.Kv;
import java.io.Serializable;
import java.util.List;
/**
* 用户基础信息
*
* @author BladeX
*/
public interface OAuth2User extends Serializable {
/**
* 获取用户ID.
*
* @return Long
*/
String getUserId();
/**
* 获取租户ID.
*
* @return String
*/
String getTenantId();
/**
* 获取第三方认证ID.
*
* @return String
*/
String getOauthId();
/**
* 获取昵称.
*
* @return String
*/
String getName();
/**
* 获取真名.
*
* @return String
*/
String getRealName();
/**
* 获取账号.
*
* @return String
*/
String getAccount();
/**
* 获取密码.
*
* @return String
*/
String getPassword();
/**
* 获取手机.
*
* @return String
*/
String getPhone();
/**
* 获取邮箱.
*
* @return String
*/
String getEmail();
/**
* 获取部门ID.
*
* @return String
*/
String getDeptId();
/**
* 获取岗位ID.
*
* @return String
*/
String getPostId();
/**
* 获取角色ID.
*
* @return String
*/
String getRoleId();
/**
* 获取角色名.
*
* @return String
*/
String getRoleName();
/**
* 获取头像.
*
* @return String
*/
String getAvatar();
/**
* 获取权限集合.
*
* @return List<String>
*/
List<String> getPermissions();
/**
* 获取角色集合.
*
* @return List<String>
*/
List<String> getAuthorities();
/**
* 获取客户端信息.
*
* @return OAuth2Client
*/
OAuth2Client getClient();
/**
* 设置客户端信息.
*/
void setClient(OAuth2Client client);
/**
* 获取用户详情.
*
* @return Kv
*/
Kv getDetail();
}

View File

@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service;
import org.springblade.core.oauth2.provider.OAuth2Request;
/**
* OAuth2UserService
*
* @author BladeX
*/
public interface OAuth2UserService {
/**
* 根据用户名获取用户信息
*
* @param userId 用户ID
* @param request 授权参数
* @return 用户信息
*/
OAuth2User loadByUserId(String userId, OAuth2Request request);
/**
* 根据用户名获取用户信息
*
* @param username 用户名
* @param request 授权参数
* @return 用户信息
*/
OAuth2User loadByUsername(String username, OAuth2Request request);
/**
* 校验用户信息
*
* @param user 用户信息
* @return 是否通过
*/
boolean validateUser(OAuth2User user);
}

View File

@@ -0,0 +1,102 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service.impl;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springblade.core.oauth2.service.OAuth2Client;
import java.io.Serial;
/**
* 客户端详情
*
* @author BladeX
*/
@Data
@Schema(description = "oauth2客户端实体类")
public class OAuth2ClientDetail implements OAuth2Client {
@Serial
private static final long serialVersionUID = 1L;
/**
* 客户端id
*/
@Schema(description = "客户端id")
private String clientId;
/**
* 客户端密钥
*/
@Schema(description = "客户端密钥")
private String clientSecret;
/**
* 资源集合
*/
@Schema(description = "资源集合")
private String resourceIds;
/**
* 授权范围
*/
@Schema(description = "授权范围")
private String scope;
/**
* 授权类型
*/
@Schema(description = "授权类型")
private String authorizedGrantTypes;
/**
* 回调地址
*/
@Schema(description = "回调地址")
private String webServerRedirectUri;
/**
* 权限
*/
@Schema(description = "权限")
private String authorities;
/**
* 令牌过期秒数
*/
@Schema(description = "令牌过期秒数")
private Integer accessTokenValidity;
/**
* 刷新令牌过期秒数
*/
@Schema(description = "刷新令牌过期秒数")
private Integer refreshTokenValidity;
/**
* 附加说明
*/
@Schema(description = "附加说明")
private String additionalInformation;
/**
* 自动授权
*/
@Schema(description = "自动授权")
private String autoapprove;
}

View File

@@ -0,0 +1,88 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service.impl;
import lombok.AllArgsConstructor;
import org.springblade.core.oauth2.constant.OAuth2ClientConstant;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Arrays;
import java.util.Optional;
/**
* 获取客户端详情
*
* @author BladeX
*/
@AllArgsConstructor
public class OAuth2ClientDetailService implements OAuth2ClientService {
private final JdbcTemplate jdbcTemplate;
@Override
public OAuth2Client loadByClientId(String clientId) {
return loadByClientId(clientId, null);
}
@Override
public OAuth2Client loadByClientId(String clientId, OAuth2Request request) {
try {
return jdbcTemplate.queryForObject(OAuth2ClientConstant.DEFAULT_SELECT_STATEMENT, new BeanPropertyRowMapper<>(OAuth2ClientDetail.class), clientId);
} catch (Exception ex) {
return null;
}
}
@Override
public boolean validateClient(OAuth2Client client, String clientId, String clientSecret) {
return Optional.ofNullable(client)
.map(c -> StringUtil.equals(clientId, c.getClientId()) && StringUtil.equals(clientSecret, c.getClientSecret()))
.orElse(false);
}
@Override
public boolean validateRedirectUri(OAuth2Client client, String redirectUri) {
return Optional.ofNullable(client)
.map(c -> StringUtil.equals(redirectUri, c.getWebServerRedirectUri()))
.orElse(false);
}
@Override
public boolean validateGranter(OAuth2Client client, String grantType) {
return Optional.ofNullable(client)
.map(c -> Arrays.stream(Func.split(c.getAuthorizedGrantTypes(), StringPool.COMMA))
.anyMatch(s -> s.trim().equals(grantType)))
.orElse(false);
}
}

View File

@@ -0,0 +1,145 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service.impl;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.tool.support.Kv;
import java.io.Serial;
import java.util.List;
/**
* 用户详情
*
* @author BladeX
*/
@Data
@Schema(description = "oauth2用户实体类")
public class OAuth2UserDetail implements OAuth2User {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
@Schema(description = "用户id")
private String userId;
/**
* 租户ID
*/
@Schema(description = "租户ID")
private String tenantId;
/**
* 第三方认证ID
*/
@Schema(description = "第三方认证ID")
private String oauthId;
/**
* 昵称
*/
@Schema(description = "昵称")
private String name;
/**
* 真名
*/
@Schema(description = "真名")
private String realName;
/**
* 账号
*/
@Schema(description = "账号")
private String account;
/**
* 密码
*/
@JsonIgnore
@Schema(description = "密码")
private String password;
/**
* 手机
*/
@Schema(description = "手机")
private String phone;
/**
* 邮箱
*/
@Schema(description = "邮箱")
private String email;
/**
* 部门id
*/
@Schema(description = "部门id")
private String deptId;
/**
* 岗位id
*/
@Schema(description = "岗位id")
private String postId;
/**
* 角色id
*/
@Schema(description = "角色id")
private String roleId;
/**
* 角色名
*/
@Schema(description = "角色名")
private String roleName;
/**
* 头像
*/
@Schema(description = "头像")
private String avatar;
/**
* 权限标识集合
*/
@Schema(description = "权限集合")
private List<String> permissions;
/**
* 角色集合
*/
@Schema(description = "角色集合")
private List<String> authorities;
/**
* 客户端
*/
@Schema(description = "客户端")
private OAuth2Client client;
/**
* 用户详情
*/
@Schema(description = "用户详情")
private Kv detail;
}

View File

@@ -0,0 +1,73 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.service.impl;
import lombok.AllArgsConstructor;
import org.springblade.core.oauth2.constant.OAuth2UserConstant;
import org.springblade.core.oauth2.provider.OAuth2Request;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Optional;
/**
* 获取用户详情
*
* @author BladeX
*/
@AllArgsConstructor
public class OAuth2UserDetailService implements OAuth2UserService {
private final JdbcTemplate jdbcTemplate;
@Override
public OAuth2User loadByUserId(String userId, OAuth2Request request) {
try {
return jdbcTemplate.queryForObject(OAuth2UserConstant.DEFAULT_USERID_SELECT_STATEMENT, new BeanPropertyRowMapper<>(OAuth2UserDetail.class), userId);
} catch (Exception ex) {
return null;
}
}
@Override
public OAuth2User loadByUsername(String username, OAuth2Request request) {
try {
return jdbcTemplate.queryForObject(OAuth2UserConstant.DEFAULT_USERNAME_SELECT_STATEMENT, new BeanPropertyRowMapper<>(OAuth2UserDetail.class), username);
} catch (Exception ex) {
return null;
}
}
@Override
public boolean validateUser(OAuth2User user) {
return Optional.ofNullable(user)
.filter(u -> u.getUserId() != null && !u.getUserId().isEmpty()) // 检查userId不为空
.filter(u -> u.getAuthorities() != null && !u.getAuthorities().isEmpty()) // 检查authorities不为空
.isPresent(); // 如果上述条件都满足则返回true否则返回false
}
}

View File

@@ -0,0 +1,49 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.utils;
/**
* OAuth2CodeUtil
*
* @author Chill
*/
public class OAuth2CodeUtil {
/**
* 授权码缓存key
*/
public static final String AUTHORIZATION_CODE_KEY = "blade:auth::code:";
/**
* code key格式
*
* @param code code
* @return key
*/
public static String codeKey(String code) {
return AUTHORIZATION_CODE_KEY + code;
}
}

View File

@@ -0,0 +1,116 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.utils;
import org.springblade.core.oauth2.exception.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import static org.springblade.core.oauth2.exception.OAuth2ErrorMessage.INVALID_ERROR_CODE;
/**
* OAuth2ExceptionUtil
*
* @author BladeX
*/
public class OAuth2ExceptionUtil {
private static final Map<ExceptionCode, Supplier<OAuth2Exception>> OAUTH2_EXCEPTION = new ConcurrentHashMap<>(16);
static {
// 初始化异常映射
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_REQUEST, () -> new OAuth2Exception(ExceptionCode.INVALID_REQUEST, ExceptionCode.INVALID_REQUEST.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.USER_NOT_FOUND, () -> new UsernameNotFoundException(ExceptionCode.USER_NOT_FOUND.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.USER_TENANT_NOT_FOUND, () -> new UserInvalidException(ExceptionCode.USER_TENANT_NOT_FOUND.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.USER_TOO_MANY_FAILS, () -> new UserInvalidException(ExceptionCode.USER_TOO_MANY_FAILS.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_USER, () -> new UserInvalidException(ExceptionCode.INVALID_USER.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.UNAUTHORIZED_USER, () -> new UserUnauthorizedException(ExceptionCode.UNAUTHORIZED_USER.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.UNAUTHORIZED_USER_TENANT, () -> new UserUnauthorizedException(ExceptionCode.UNAUTHORIZED_USER_TENANT.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_REFRESH_TOKEN, () -> new GranterInvalidException(ExceptionCode.INVALID_REFRESH_TOKEN.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.CLIENT_NOT_FOUND, () -> new ClientNotFoundException(ExceptionCode.CLIENT_NOT_FOUND.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_CLIENT, () -> new ClientInvalidException(ExceptionCode.INVALID_CLIENT.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_CLIENT_REDIRECT_URI, () -> new ClientInvalidException(ExceptionCode.INVALID_CLIENT_REDIRECT_URI.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.UNAUTHORIZED_CLIENT, () -> new ClientUnauthorizedException(ExceptionCode.UNAUTHORIZED_CLIENT.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.UNSUPPORTED_GRANT_TYPE, () -> new OAuth2Exception(ExceptionCode.UNSUPPORTED_GRANT_TYPE, ExceptionCode.UNSUPPORTED_GRANT_TYPE.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_GRANTER, () -> new GranterInvalidException(ExceptionCode.INVALID_GRANTER.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.INVALID_SCOPE, () -> new OAuth2Exception(ExceptionCode.INVALID_SCOPE, ExceptionCode.INVALID_SCOPE.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.SERVER_ERROR, () -> new OAuth2Exception(ExceptionCode.SERVER_ERROR, ExceptionCode.SERVER_ERROR.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.ACCESS_DENIED, () -> new OAuth2Exception(ExceptionCode.ACCESS_DENIED, ExceptionCode.ACCESS_DENIED.getMessage())
);
OAUTH2_EXCEPTION.put(
ExceptionCode.TEMPORARILY_UNAVAILABLE, () -> new OAuth2Exception(ExceptionCode.TEMPORARILY_UNAVAILABLE, ExceptionCode.TEMPORARILY_UNAVAILABLE.getMessage())
);
}
/**
* 根据错误代码抛出异常
*
* @param code 错误代码
*/
public static void throwFromCode(int code) {
Supplier<OAuth2Exception> exceptionSupplier = OAUTH2_EXCEPTION.get(ExceptionCode.of(code));
if (exceptionSupplier != null) {
throw exceptionSupplier.get();
} else {
throw new IllegalArgumentException(String.format(INVALID_ERROR_CODE, code));
}
}
}

View File

@@ -0,0 +1,107 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.oauth2.utils;
import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.secure.TokenInfo;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.support.Kv;
import static org.springblade.core.launch.constant.TokenConstant.*;
/**
* OAuth2Util
*
* @author BladeX
*/
public class OAuth2Util extends SecureUtil {
/**
* 创建accessToken
*
* @param user 用户信息
* @return accessToken
*/
public static TokenInfo createAccessToken(OAuth2User user) {
Kv kv = Kv.create().set(TOKEN_TYPE, ACCESS_TOKEN)
.set(CLIENT_ID, user.getClient().getClientId())
.set(TENANT_ID, user.getTenantId())
.set(USER_ID, user.getUserId())
.set(DEPT_ID, user.getDeptId())
.set(POST_ID, user.getPostId())
.set(ROLE_ID, user.getRoleId())
.set(OAUTH_ID, user.getOauthId())
.set(ACCOUNT, user.getAccount())
.set(USER_NAME, user.getAccount())
.set(NICK_NAME, user.getName())
.set(REAL_NAME, user.getRealName())
.set(ROLE_NAME, user.getRoleName())
.set(DETAIL, user.getDetail());
return createToken(kv, user.getClient().getAccessTokenValidity());
}
/**
* 创建refreshToken
*
* @param user 用户信息
* @return refreshToken
*/
public static TokenInfo createRefreshToken(OAuth2User user) {
Kv kv = Kv.create().set(TOKEN_TYPE, REFRESH_TOKEN)
.set(USER_ID, user.getUserId())
.set(DEPT_ID, user.getDeptId())
.set(ROLE_ID, user.getRoleId());
return createToken(kv, user.getClient().getRefreshTokenValidity());
}
/**
* 创建clientAccessToken
*
* @param client 客户端信息
* @return clientToken
*/
public static TokenInfo createClientAccessToken(OAuth2Client client) {
Kv kv = Kv.create().set(TOKEN_TYPE, CLIENT_ACCESS_TOKEN)
.set(CLIENT_ID, client.getClientId());
return createToken(kv, client.getAccessTokenValidity());
}
/**
* 创建implicitAccessToken
*
* @param user 用户信息
* @return implicitAccessToken
*/
public static TokenInfo createImplicitAccessToken(OAuth2User user) {
Kv kv = Kv.create().set(TOKEN_TYPE, IMPLICIT_ACCESS_TOKEN)
.set(ACCOUNT, user.getAccount());
return createToken(kv);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,571 @@
/*------------------------------------------------------------------
* Theme Name: iofrm - form templates
* Theme URI: http://www.brandio.io/envato/iofrm
* Author: Brandio
* Author URI: http://www.brandio.io/
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
-------------------------------------------------------------------*/
body {
background-color: #152733;
}
.form-body {
background-color: #152733;
}
.website-logo {
display: none;
top: 50px;
left: 50px;
right: initial;
bottom: initial;
}
.website-logo img {
width: 100px;
}
.website-logo .logo {
background-image: url("../images/logo-light.svg");
}
.website-logo .logo img {
width: 100px;
}
.website-logo-inside img {
width: 100px;
}
.website-logo-inside .logo {
background-image: url("../images/logo-light.svg");
}
.website-logo-inside .logo img {
width: 100px;
}
.img-holder {
width: 0;
background-color: #5CBAFF;
}
.img-holder .info-holder h3 {
color: #fff;
text-align: left;
}
.img-holder .info-holder h3 span {
color: #fff;
}
.img-holder .info-holder h2 {
color: #fff;
text-align: left;
}
.img-holder .info-holder h2 span {
color: #fff;
}
.img-holder .info-holder p {
color: #fff;
text-align: left;
}
.img-holder .bg {
opacity: 0.23;
background-image: none;
}
.form-holder {
margin-left: 0;
}
.form-holder .form-content ::-webkit-input-placeholder {
color: #8D8D8D !important;
}
.form-holder .form-content :-moz-placeholder {
color: #8D8D8D !important;
}
.form-holder .form-content ::-moz-placeholder {
color: #8D8D8D !important;
}
.form-holder .form-content :-ms-input-placeholder {
color: #8D8D8D !important;
}
.form-content {
background-color: #152733;
}
.form-content .form-group {
color: #fff;
}
.form-content .form-items {
max-width: 380px;
text-align: center;
}
.form-content .form-icon {
margin-top: calc(-42px - 35px);
}
.form-content .form-icon .icon-holder {
background-color: #4A77F7;
}
.form-content h1 {
color: #fff;
text-align: center;
}
.form-content h2 {
color: #fff;
text-align: center;
}
.form-content h3 {
color: #fff;
text-align: center;
}
.form-content p {
color: #fff;
text-align: center;
}
.form-content label {
color: #fff;
text-align: center;
}
.form-content .page-links a {
color: #fff;
}
.form-content .page-links a:after {
background-color: rgba(255, 255, 255, 0.5);
}
.form-content .page-links a.active:after {
background-color: #fff;
}
.form-content .page-links a:hover:after, .form-content .page-links a:focus:after {
background-color: #fff;
}
.form-content input, .form-content .dropdown-toggle.btn-default {
border: 0;
background-color: #fff;
color: #8D8D8D;
}
.form-content input:hover, .form-content input:focus, .form-content .dropdown-toggle.btn-default:hover, .form-content .dropdown-toggle.btn-default:focus {
border: 0;
background-color: #ebeff8;
color: #8D8D8D;
}
.form-content textarea {
border: 0;
background-color: #fff;
color: #8D8D8D;
}
.form-content textarea:hover, .form-content textarea:focus {
border: 0;
background-color: #ebeff8;
color: #8D8D8D;
}
.form-content .custom-file-label {
border: 0;
background-color: #fff;
color: #8D8D8D;
}
.form-content .custom-file-label:after {
color: #0093FF;
}
.form-content .custom-file:hover .custom-file-label, .form-content .custom-file:focus .custom-file-label {
border: 0;
background-color: #ebeff8;
color: #8D8D8D;
}
.form-content input[type=checkbox]:not(:checked) + label, .form-content input[type=checkbox]:checked + label, .form-content input[type=radio]:not(:checked) + label, .form-content input[type=radio]:checked + label {
color: #fff;
font-weight: 700;
}
.form-content input[type=checkbox]:checked + label, .form-content input[type=radio]:checked + label {
color: #fff;
}
.form-content input[type=checkbox]:checked + label:before, .form-content input[type=radio]:checked + label:before {
background: #fff;
border: 0px solid #fff;
}
.form-content input[type=checkbox]:not(:checked) + label:before, .form-content input[type=radio]:not(:checked) + label:before {
background: transparent;
border: 2px solid #fff;
}
.form-content input[type=checkbox]:not(:checked) + label:after, .form-content input[type=checkbox]:checked + label:after {
color: #152733;
}
.form-content input[type=radio]:not(:checked) + label:after, .form-content input[type=radio]:checked + label:after {
background-color: #152733;
}
.form-content .custom-options input[type=checkbox]:not(:checked) + label, .form-content .custom-options input[type=checkbox]:checked + label, .form-content .custom-options input[type=radio]:not(:checked) + label, .form-content .custom-options input[type=radio]:checked + label {
color: #606060;
background-color: #F7F7F7;
}
.form-content .custom-options input[type=checkbox]:checked + label, .form-content .custom-options input[type=radio]:checked + label {
color: #fff;
background-color: #1592E6;
-webkit-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.16);
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.16);
}
.form-content .form-button .lbtn {
margin-top: 10px;
background-color: #cb3444;
color: #fff;
-webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
}
.form-content .form-button .lbtn:hover, .form-content .form-button .lbtn:focus {
background-color: #cb3444;
color: #fff;
-webkit-box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
}
.form-content .form-button .ibtn {
background-color: #1592E6;
color: #fff;
-webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
}
.form-content .form-button .ibtn:hover, .form-content .form-button .ibtn:focus {
background-color: #1592E6;
color: #fff;
-webkit-box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
}
.form-content .form-button a {
color: #fff;
}
.form-content .other-links span {
color: #fff;
}
.form-content .other-links a {
color: #fff;
}
.form-content .form-sent .tick-holder .tick-icon {
background-color: rgba(14, 30, 41, 0);
}
.form-content .form-sent .tick-holder .tick-icon:before {
background-color: #8CCB57;
}
.form-content .form-sent .tick-holder .tick-icon:after {
background-color: #8CCB57;
}
.form-content .form-sent h3 {
color: #fff;
}
.form-content .form-sent p {
color: #fff;
}
.form-content .form-sent .info-holder {
color: #fff;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.form-content .form-sent .info-holder span {
color: #fff;
}
.form-content .form-sent .info-holder a {
color: #fff;
}
@keyframes tick-anime3 {
0% {
background-color: rgba(14, 30, 41, 0);
-webkit-transform: rotate(35deg) scale(2);
-moz-transform: rotate(35deg) scale(2);
-ms-transform: rotate(35deg) scale(2);
transform: rotate(35deg) scale(2);
}
100% {
background-color: #0E1E29;
-webkit-transform: rotate(45deg) scale(1);
-moz-transform: rotate(45deg) scale(1);
-ms-transform: rotate(45deg) scale(1);
transform: rotate(45deg) scale(1);
}
}
.alert {
color: #fff;
}
.alert.alert-primary {
background-color: rgba(226, 240, 255, 0);
border-color: #3a86d6;
}
.alert.alert-primary hr {
border-top-color: #3a86d6;
}
.alert.alert-secondary {
background-color: rgba(240, 240, 240, 0);
border-color: #8e9396;
}
.alert.alert-secondary hr {
border-top-color: #8e9396;
}
.alert.alert-success {
background-color: rgba(247, 255, 240, 0);
border-color: #8CCB57;
}
.alert.alert-success hr {
border-top-color: #8CCB57;
}
.alert.alert-danger {
background-color: rgba(255, 250, 250, 0);
border-color: #F55050;
}
.alert.alert-danger hr {
border-top-color: #F55050;
}
.alert.alert-warning {
background-color: rgba(255, 248, 225, 0);
border-color: #f1cb4b;
}
.alert.alert-warning hr {
border-top-color: #f1cb4b;
}
.alert.alert-info {
background-color: rgba(220, 237, 241, 0);
border-color: #42bfdb;
}
.alert.alert-info hr {
border-top-color: #42bfdb;
}
.alert.alert-light {
background-color: rgba(254, 254, 254, 0);
border-color: #a7a4a4;
}
.alert.alert-light hr {
border-top-color: #a7a4a4;
}
.alert.alert-dark {
background-color: rgba(214, 216, 217, 0);
border-color: #525557;
}
.alert.alert-dark hr {
border-top-color: #525557;
}
.alert.with-icon.alert-primary:before {
color: #3a86d6;
}
.alert.with-icon.alert-secondary:before {
color: #8e9396;
}
.alert.with-icon.alert-success:before {
color: #8CCB57;
}
.alert.with-icon.alert-danger:before {
color: #F55050;
}
.alert.with-icon.alert-warning:before {
color: #f1cb4b;
}
.alert.with-icon.alert-info:before {
color: #42bfdb;
}
.alert.with-icon.alert-light:before {
color: #a7a4a4;
}
.alert.with-icon.alert-dark:before {
color: #525557;
}
.alert a, .alert a.alert-link {
color: #fff;
}
.alert .close {
color: #727272;
}
.alert .close span {
color: #727272;
}
.form-subtitle {
color: #fff;
}
.rad-with-details .more-info {
color: #fff;
}
.form-body.without-side h3 {
color: #000;
}
.form-body.without-side p {
color: #000;
}
.form-body.without-side label {
color: #000;
}
.form-body.without-side .img-holder .info-holder img {
display: inline-block;
}
.form-body.without-side .form-content .form-items {
padding: 35px 30px;
background-color: #fff;
}
.form-body.without-side .form-content .form-items .other-links .text {
color: #000;
}
.form-body.without-side .form-content .form-items .other-links a {
color: #000;
background-color: #F7F7F7;
}
.form-body.without-side .form-content .page-links a {
color: #000;
}
.form-body.without-side .form-content .page-links a:after {
background-color: rgba(255, 255, 255, 0.5);
}
.form-body.without-side .form-content .page-links a.active:after {
background-color: #fff;
}
.form-body.without-side .form-content .page-links a:hover:after, .form-body.without-side .form-content .page-links a:focus:after {
background-color: #fff;
}
.form-body.without-side .form-content input, .form-body.without-side .form-content .dropdown-toggle.btn-default {
border: 0;
background-color: #fff;
color: #8D8D8D;
}
.form-body.without-side .form-content input:hover, .form-body.without-side .form-content input:focus, .form-body.without-side .form-content .dropdown-toggle.btn-default:hover, .form-body.without-side .form-content .dropdown-toggle.btn-default:focus {
border: 0;
background-color: #fff;
color: #8D8D8D;
}
.form-body.without-side .form-content .form-button .lbtn {
background-color: #cb3444;
color: #fff;
-webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
}
.form-body.without-side .form-content .form-button .lbtn:hover, .form-body.without-side .form-content .form-button .lbtn:focus {
-webkit-box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
}
.form-body.without-side .form-content .form-button .ibtn {
background-color: #1592E6;
color: #fff;
-webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
box-shadow: 0 0 0 rgba(0, 0, 0, 0.16);
}
.form-body.without-side .form-content .form-button .ibtn:hover, .form-body.without-side .form-content .form-button .ibtn:focus {
-webkit-box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
box-shadow: 0 5px 6px rgba(0, 0, 0, 0.16);
}
.form-body.without-side .form-content .form-button a {
color: #fff;
}
/* -----------------------------------
2 - Responsive Styles
------------------------------------*/
@media (max-width: 992px) {
.form-holder {
margin-left: 0;
}
.website-logo {
top: 50px;
left: 50px;
right: initial;
bottom: initial;
}
.website-logo .logo {
background-image: url("../images/logo-light.svg");
}
.form-body.without-side .website-logo .logo {
background-image: url("../images/logo-light.svg");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,683 @@
/**
* [js-md5]{@link https://github.com/emn178/js-md5}
*
* @namespace md5
* @version 0.7.3
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2014-2017
* @license MIT
*/
(function () {
'use strict';
var ERROR = 'input is invalid type';
var WINDOW = typeof window === 'object';
var root = WINDOW ? window : {};
if (root.JS_MD5_NO_WINDOW) {
WINDOW = false;
}
var WEB_WORKER = !WINDOW && typeof self === 'object';
var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
if (NODE_JS) {
root = global;
} else if (WEB_WORKER) {
root = self;
}
var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
var AMD = typeof define === 'function' && define.amd;
var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
var HEX_CHARS = '0123456789abcdef'.split('');
var EXTRA = [128, 32768, 8388608, -2147483648];
var SHIFT = [0, 8, 16, 24];
var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
var blocks = [], buffer8;
if (ARRAY_BUFFER) {
var buffer = new ArrayBuffer(68);
buffer8 = new Uint8Array(buffer);
blocks = new Uint32Array(buffer);
}
if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
Array.isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
ArrayBuffer.isView = function (obj) {
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
};
}
/**
* @method hex
* @memberof md5
* @description Output hash as hex string
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {String} Hex string
* @example
* md5.hex('The quick brown fox jumps over the lazy dog');
* // equal to
* md5('The quick brown fox jumps over the lazy dog');
*/
/**
* @method digest
* @memberof md5
* @description Output hash as bytes array
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {Array} Bytes array
* @example
* md5.digest('The quick brown fox jumps over the lazy dog');
*/
/**
* @method array
* @memberof md5
* @description Output hash as bytes array
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {Array} Bytes array
* @example
* md5.array('The quick brown fox jumps over the lazy dog');
*/
/**
* @method arrayBuffer
* @memberof md5
* @description Output hash as ArrayBuffer
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {ArrayBuffer} ArrayBuffer
* @example
* md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
*/
/**
* @method buffer
* @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
* @memberof md5
* @description Output hash as ArrayBuffer
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {ArrayBuffer} ArrayBuffer
* @example
* md5.buffer('The quick brown fox jumps over the lazy dog');
*/
/**
* @method base64
* @memberof md5
* @description Output hash as base64 string
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {String} base64 string
* @example
* md5.base64('The quick brown fox jumps over the lazy dog');
*/
var createOutputMethod = function (outputType) {
return function (message) {
return new Md5(true).update(message)[outputType]();
};
};
/**
* @method create
* @memberof md5
* @description Create Md5 object
* @returns {Md5} Md5 object.
* @example
* var hash = md5.create();
*/
/**
* @method update
* @memberof md5
* @description Create and update Md5 object
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {Md5} Md5 object.
* @example
* var hash = md5.update('The quick brown fox jumps over the lazy dog');
* // equal to
* var hash = md5.create();
* hash.update('The quick brown fox jumps over the lazy dog');
*/
var createMethod = function () {
var method = createOutputMethod('hex');
if (NODE_JS) {
method = nodeWrap(method);
}
method.create = function () {
return new Md5();
};
method.update = function (message) {
return method.create().update(message);
};
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
var type = OUTPUT_TYPES[i];
method[type] = createOutputMethod(type);
}
return method;
};
var nodeWrap = function (method) {
var crypto = eval("require('crypto')");
var Buffer = eval("require('buffer').Buffer");
var nodeMethod = function (message) {
if (typeof message === 'string') {
return crypto.createHash('md5').update(message, 'utf8').digest('hex');
} else {
if (message === null || message === undefined) {
throw ERROR;
} else if (message.constructor === ArrayBuffer) {
message = new Uint8Array(message);
}
}
if (Array.isArray(message) || ArrayBuffer.isView(message) ||
message.constructor === Buffer) {
return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
} else {
return method(message);
}
};
return nodeMethod;
};
/**
* Md5 class
* @class Md5
* @description This is internal class.
* @see {@link md5.create}
*/
function Md5(sharedMemory) {
if (sharedMemory) {
blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
this.blocks = blocks;
this.buffer8 = buffer8;
} else {
if (ARRAY_BUFFER) {
var buffer = new ArrayBuffer(68);
this.buffer8 = new Uint8Array(buffer);
this.blocks = new Uint32Array(buffer);
} else {
this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
}
}
this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
this.finalized = this.hashed = false;
this.first = true;
}
/**
* @method update
* @memberof Md5
* @instance
* @description Update hash
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {Md5} Md5 object.
* @see {@link md5.update}
*/
Md5.prototype.update = function (message) {
if (this.finalized) {
return;
}
var notString, type = typeof message;
if (type !== 'string') {
if (type === 'object') {
if (message === null) {
throw ERROR;
} else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
message = new Uint8Array(message);
} else if (!Array.isArray(message)) {
if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
throw ERROR;
}
}
} else {
throw ERROR;
}
notString = true;
}
var code, index = 0, i, length = message.length, blocks = this.blocks;
var buffer8 = this.buffer8;
while (index < length) {
if (this.hashed) {
this.hashed = false;
blocks[0] = blocks[16];
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
}
if (notString) {
if (ARRAY_BUFFER) {
for (i = this.start; index < length && i < 64; ++index) {
buffer8[i++] = message[index];
}
} else {
for (i = this.start; index < length && i < 64; ++index) {
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
}
}
} else {
if (ARRAY_BUFFER) {
for (i = this.start; index < length && i < 64; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
buffer8[i++] = code;
} else if (code < 0x800) {
buffer8[i++] = 0xc0 | (code >> 6);
buffer8[i++] = 0x80 | (code & 0x3f);
} else if (code < 0xd800 || code >= 0xe000) {
buffer8[i++] = 0xe0 | (code >> 12);
buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
buffer8[i++] = 0x80 | (code & 0x3f);
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
buffer8[i++] = 0xf0 | (code >> 18);
buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);
buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
buffer8[i++] = 0x80 | (code & 0x3f);
}
}
} else {
for (i = this.start; index < length && i < 64; ++index) {
code = message.charCodeAt(index);
if (code < 0x80) {
blocks[i >> 2] |= code << SHIFT[i++ & 3];
} else if (code < 0x800) {
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else if (code < 0xd800 || code >= 0xe000) {
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
} else {
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
}
}
}
}
this.lastByteIndex = i;
this.bytes += i - this.start;
if (i >= 64) {
this.start = i - 64;
this.hash();
this.hashed = true;
} else {
this.start = i;
}
}
if (this.bytes > 4294967295) {
this.hBytes += this.bytes / 4294967296 << 0;
this.bytes = this.bytes % 4294967296;
}
return this;
};
Md5.prototype.finalize = function () {
if (this.finalized) {
return;
}
this.finalized = true;
var blocks = this.blocks, i = this.lastByteIndex;
blocks[i >> 2] |= EXTRA[i & 3];
if (i >= 56) {
if (!this.hashed) {
this.hash();
}
blocks[0] = blocks[16];
blocks[16] = blocks[1] = blocks[2] = blocks[3] =
blocks[4] = blocks[5] = blocks[6] = blocks[7] =
blocks[8] = blocks[9] = blocks[10] = blocks[11] =
blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
}
blocks[14] = this.bytes << 3;
blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
this.hash();
};
Md5.prototype.hash = function () {
var a, b, c, d, bc, da, blocks = this.blocks;
if (this.first) {
a = blocks[0] - 680876937;
a = (a << 7 | a >>> 25) - 271733879 << 0;
d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
d = (d << 12 | d >>> 20) + a << 0;
c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
c = (c << 17 | c >>> 15) + d << 0;
b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
b = (b << 22 | b >>> 10) + c << 0;
} else {
a = this.h0;
b = this.h1;
c = this.h2;
d = this.h3;
a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
a = (a << 7 | a >>> 25) + b << 0;
d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
d = (d << 12 | d >>> 20) + a << 0;
c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
c = (c << 17 | c >>> 15) + d << 0;
b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
b = (b << 22 | b >>> 10) + c << 0;
}
a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
a = (a << 7 | a >>> 25) + b << 0;
d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
d = (d << 12 | d >>> 20) + a << 0;
c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
c = (c << 17 | c >>> 15) + d << 0;
b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
b = (b << 22 | b >>> 10) + c << 0;
a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
a = (a << 7 | a >>> 25) + b << 0;
d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
d = (d << 12 | d >>> 20) + a << 0;
c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
c = (c << 17 | c >>> 15) + d << 0;
b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
b = (b << 22 | b >>> 10) + c << 0;
a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
a = (a << 7 | a >>> 25) + b << 0;
d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
d = (d << 12 | d >>> 20) + a << 0;
c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
c = (c << 17 | c >>> 15) + d << 0;
b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
b = (b << 22 | b >>> 10) + c << 0;
a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
a = (a << 5 | a >>> 27) + b << 0;
d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
d = (d << 9 | d >>> 23) + a << 0;
c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
c = (c << 14 | c >>> 18) + d << 0;
b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
b = (b << 20 | b >>> 12) + c << 0;
a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
a = (a << 5 | a >>> 27) + b << 0;
d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
d = (d << 9 | d >>> 23) + a << 0;
c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
c = (c << 14 | c >>> 18) + d << 0;
b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
b = (b << 20 | b >>> 12) + c << 0;
a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
a = (a << 5 | a >>> 27) + b << 0;
d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
d = (d << 9 | d >>> 23) + a << 0;
c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
c = (c << 14 | c >>> 18) + d << 0;
b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
b = (b << 20 | b >>> 12) + c << 0;
a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
a = (a << 5 | a >>> 27) + b << 0;
d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
d = (d << 9 | d >>> 23) + a << 0;
c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
c = (c << 14 | c >>> 18) + d << 0;
b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
b = (b << 20 | b >>> 12) + c << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[5] - 378558;
a = (a << 4 | a >>> 28) + b << 0;
d += (bc ^ a) + blocks[8] - 2022574463;
d = (d << 11 | d >>> 21) + a << 0;
da = d ^ a;
c += (da ^ b) + blocks[11] + 1839030562;
c = (c << 16 | c >>> 16) + d << 0;
b += (da ^ c) + blocks[14] - 35309556;
b = (b << 23 | b >>> 9) + c << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[1] - 1530992060;
a = (a << 4 | a >>> 28) + b << 0;
d += (bc ^ a) + blocks[4] + 1272893353;
d = (d << 11 | d >>> 21) + a << 0;
da = d ^ a;
c += (da ^ b) + blocks[7] - 155497632;
c = (c << 16 | c >>> 16) + d << 0;
b += (da ^ c) + blocks[10] - 1094730640;
b = (b << 23 | b >>> 9) + c << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[13] + 681279174;
a = (a << 4 | a >>> 28) + b << 0;
d += (bc ^ a) + blocks[0] - 358537222;
d = (d << 11 | d >>> 21) + a << 0;
da = d ^ a;
c += (da ^ b) + blocks[3] - 722521979;
c = (c << 16 | c >>> 16) + d << 0;
b += (da ^ c) + blocks[6] + 76029189;
b = (b << 23 | b >>> 9) + c << 0;
bc = b ^ c;
a += (bc ^ d) + blocks[9] - 640364487;
a = (a << 4 | a >>> 28) + b << 0;
d += (bc ^ a) + blocks[12] - 421815835;
d = (d << 11 | d >>> 21) + a << 0;
da = d ^ a;
c += (da ^ b) + blocks[15] + 530742520;
c = (c << 16 | c >>> 16) + d << 0;
b += (da ^ c) + blocks[2] - 995338651;
b = (b << 23 | b >>> 9) + c << 0;
a += (c ^ (b | ~d)) + blocks[0] - 198630844;
a = (a << 6 | a >>> 26) + b << 0;
d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
d = (d << 10 | d >>> 22) + a << 0;
c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
c = (c << 15 | c >>> 17) + d << 0;
b += (d ^ (c | ~a)) + blocks[5] - 57434055;
b = (b << 21 | b >>> 11) + c << 0;
a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
a = (a << 6 | a >>> 26) + b << 0;
d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
d = (d << 10 | d >>> 22) + a << 0;
c += (a ^ (d | ~b)) + blocks[10] - 1051523;
c = (c << 15 | c >>> 17) + d << 0;
b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
b = (b << 21 | b >>> 11) + c << 0;
a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
a = (a << 6 | a >>> 26) + b << 0;
d += (b ^ (a | ~c)) + blocks[15] - 30611744;
d = (d << 10 | d >>> 22) + a << 0;
c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
c = (c << 15 | c >>> 17) + d << 0;
b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
b = (b << 21 | b >>> 11) + c << 0;
a += (c ^ (b | ~d)) + blocks[4] - 145523070;
a = (a << 6 | a >>> 26) + b << 0;
d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
d = (d << 10 | d >>> 22) + a << 0;
c += (a ^ (d | ~b)) + blocks[2] + 718787259;
c = (c << 15 | c >>> 17) + d << 0;
b += (d ^ (c | ~a)) + blocks[9] - 343485551;
b = (b << 21 | b >>> 11) + c << 0;
if (this.first) {
this.h0 = a + 1732584193 << 0;
this.h1 = b - 271733879 << 0;
this.h2 = c - 1732584194 << 0;
this.h3 = d + 271733878 << 0;
this.first = false;
} else {
this.h0 = this.h0 + a << 0;
this.h1 = this.h1 + b << 0;
this.h2 = this.h2 + c << 0;
this.h3 = this.h3 + d << 0;
}
};
/**
* @method hex
* @memberof Md5
* @instance
* @description Output hash as hex string
* @returns {String} Hex string
* @see {@link md5.hex}
* @example
* hash.hex();
*/
Md5.prototype.hex = function () {
this.finalize();
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];
};
/**
* @method toString
* @memberof Md5
* @instance
* @description Output hash as hex string
* @returns {String} Hex string
* @see {@link md5.hex}
* @example
* hash.toString();
*/
Md5.prototype.toString = Md5.prototype.hex;
/**
* @method digest
* @memberof Md5
* @instance
* @description Output hash as bytes array
* @returns {Array} Bytes array
* @see {@link md5.digest}
* @example
* hash.digest();
*/
Md5.prototype.digest = function () {
this.finalize();
var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
return [
h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,
h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,
h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,
h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF
];
};
/**
* @method array
* @memberof Md5
* @instance
* @description Output hash as bytes array
* @returns {Array} Bytes array
* @see {@link md5.array}
* @example
* hash.array();
*/
Md5.prototype.array = Md5.prototype.digest;
/**
* @method arrayBuffer
* @memberof Md5
* @instance
* @description Output hash as ArrayBuffer
* @returns {ArrayBuffer} ArrayBuffer
* @see {@link md5.arrayBuffer}
* @example
* hash.arrayBuffer();
*/
Md5.prototype.arrayBuffer = function () {
this.finalize();
var buffer = new ArrayBuffer(16);
var blocks = new Uint32Array(buffer);
blocks[0] = this.h0;
blocks[1] = this.h1;
blocks[2] = this.h2;
blocks[3] = this.h3;
return buffer;
};
/**
* @method buffer
* @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
* @memberof Md5
* @instance
* @description Output hash as ArrayBuffer
* @returns {ArrayBuffer} ArrayBuffer
* @see {@link md5.buffer}
* @example
* hash.buffer();
*/
Md5.prototype.buffer = Md5.prototype.arrayBuffer;
/**
* @method base64
* @memberof Md5
* @instance
* @description Output hash as base64 string
* @returns {String} base64 string
* @see {@link md5.base64}
* @example
* hash.base64();
*/
Md5.prototype.base64 = function () {
var v1, v2, v3, base64Str = '', bytes = this.array();
for (var i = 0; i < 15;) {
v1 = bytes[i++];
v2 = bytes[i++];
v3 = bytes[i++];
base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +
BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +
BASE64_ENCODE_CHAR[v3 & 63];
}
v1 = bytes[i];
base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
BASE64_ENCODE_CHAR[(v1 << 4) & 63] +
'==';
return base64Str;
};
var exports = createMethod();
if (COMMON_JS) {
module.exports = exports;
} else {
/**
* @method md5
* @description Md5 hash function, export to global in browsers.
* @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
* @returns {String} md5 hashes
* @example
* md5(''); // d41d8cd98f00b204e9800998ecf8427e
* md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
* md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
*
* // It also supports UTF-8 encoding
* md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
*
* // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
* md5([]); // d41d8cd98f00b204e9800998ecf8427e
* md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
*/
root.md5 = exports;
if (AMD) {
define(function () {
return exports;
});
}
}
})();

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BladeX 统一认证系统</title>
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-style.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-theme.css">
</head>
<body>
<div class="form-body">
<div class="row">
<div class="form-holder">
<div class="form-content">
<div class="form-items">
<div class="website-logo-inside">
<img class="logo-size" src="/static/images/bladex-logo.png">
<h1>BladeX 统一认证系统</h1>
</div>
<p th:text="'应用 [' + ${client_id} + '] 请求授权'">应用 [未获取到应用信息] 请求授权</p>
<p>授权后该应用将取得系统操作权限</p>
<div class="page-links">
<a th:text="'授权账号 [' + ${username} + '] 请求授权'">授权账号 [未获取到账号信息]</a>
</div>
<form action="/oauth/authorize/perform" method="post">
<input name='approval' value='true' type='hidden'/>
<input id='state' name='state' th:value="${state}" type='hidden'/>
<div class="form-button">
<button id="submit" type="submit" class="ibtn">同意/授权</button>
<button id="logout" class="lbtn">退出登录</button>
</div>
</form>
</div>
<div class="other-links">
<span>Copyrights © 2024 <a href="https://bladex.cn" target="_blank">BladeX</a> All Rights Reserved.</span>
</div>
</div>
</div>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script>
$(window).on("load", function () {
// 初始化页面增加租户id传递
const state = getUrlParam("state");
if (state !== null && state !== "") {
$("#state").val(state);
}
});
$("#logout").click(function(){
window.location.href = "/oauth/authorize/logout";
});
// 获取url中参数的值
function getUrlParam(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BladeX 统一认证系统</title>
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-style.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-theme.css">
</head>
<body>
<div class="form-body">
<div class="row">
<div class="form-holder">
<div class="form-content">
<div class="form-items">
<div class="website-logo-inside">
<img class="logo-size" src="/static/images/bladex-logo.png">
<h1>BladeX 统一认证系统</h1>
</div>
<p>应用授权失败</p>
<div class="page-links">
<a>请返回 [认证页面] 重新认证</a>
</div>
<div class="form-button">
<button id="logout" class="lbtn">前往认证</button>
</div>
</div>
<div class="other-links">
<span>Copyrights © 2024 <a href="https://bladex.cn" target="_blank">BladeX</a> All Rights Reserved.</span>
</div>
</div>
</div>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script>
$("#logout").click(function(){
window.location.href = "/oauth/authorize/logout";
});
</script>
</body>
</html>

View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BladeX 统一认证系统</title>
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-style.css">
<link rel="stylesheet" type="text/css" href="/static/css/iofrm-theme.css">
</head>
<body>
<div class="form-body">
<div class="row">
<div class="form-holder">
<div class="form-content">
<div class="form-items">
<div class="website-logo-inside">
<img class="logo-size" src="/static/images/bladex-logo.png">
<h1>BladeX 统一认证系统</h1>
</div>
<p>欢迎使用统一认证,提交后请对应用进行授权</p>
<div class="page-links">
<a>请输入认证信息</a>
</div>
<form id="form" action="/oauth/login/perform" method="post">
<input class="form-control" type="text" name="tenant_id" placeholder="请输入租户ID" required>
<input class="form-control" type="text" name="username" placeholder="请输入用户名" required>
<input class="form-control" id="ipt" type="password" name="password" placeholder="请输入密码" required>
<div class="form-button">
<button id="btn" type="button" class="ibtn">登 录</button>
</div>
</form>
</div>
<div class="other-links">
<span>Copyrights © 2024 <a href="https://bladex.cn" target="_blank">BladeX</a> All Rights Reserved.</span>
</div>
</div>
</div>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/md5.js"></script>
<script>
$("#btn").click(function(){
const val = $("#ipt").val();
$("#ipt").val(md5(val));
$("#btn").attr("disabled", "disabled");
$("#btn").text("登 录 中");
$("#form").submit();
});
</script>
</body>
</html>