yangys
2025-08-02 43ef1ca4c27fdeed50f81f8f6bd6ce52c1480f5c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * 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.auth.granter;
 
import org.jetbrains.annotations.NotNull;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
import org.springblade.core.oauth2.handler.PasswordHandler;
import org.springblade.core.oauth2.props.OAuth2Properties;
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.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.NumberUtil;
import org.springblade.core.tool.utils.SM2Util;
import org.springblade.system.cache.ParamCache;
import org.springblade.system.feign.IUserClient;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.enums.UserType;
import org.springframework.stereotype.Component;
 
import java.util.Collections;
import java.util.function.Predicate;
 
import static org.springblade.auth.constant.BladeAuthConstant.REGISTER_USER_VALUE;
 
/**
 * RegisterTokenGranter
 *
 * @author BladeX
 */
@Component
public class RegisterTokenGranter extends AbstractTokenGranter {
 
    private final IUserClient userClient;
    private final OAuth2Properties properties;
 
    public RegisterTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, IUserClient userClient, OAuth2Properties properties) {
        super(clientService, userService, passwordHandler);
        this.userClient = userClient;
        this.properties = properties;
    }
 
    @Override
    public String type() {
        return REGISTER;
    }
 
    @Override
    public OAuth2User user(OAuth2Request request) {
        // 校验注册功能是否开启
        Boolean registerOpen = Func.toBoolean(ParamCache.getValue(REGISTER_USER_VALUE), false);
        if (!registerOpen) {
            throw new UserInvalidException("注册功能暂未开启,请联系管理员");
        }
 
        // 用户注册信息
        User user = new User();
        user.setUserType(UserType.of(request.getUserType()).getCategory());
        user.setTenantId(request.getTenantId());
        user.setAccount(request.getUsername());
        user.setPassword(SM2Util.decrypt(request.getPassword(), properties.getPublicKey(), properties.getPrivateKey()));
        user.setName(request.getName());
        user.setRealName(request.getName());
        user.setPhone(request.getPhone());
        user.setEmail(request.getEmail());
 
        // 校验用户格式
        validateUser(user);
 
        R<String> result = userClient.registerUser(user);
 
        // 执行用户注册
        if (result.isSuccess()) {
            // 构建oauth2所需用户信息
            user.setId(NumberUtil.toLong(result.getData()));
            return convertOAuth2UserDetail(user, client(request));
        }
        throw new UserInvalidException(result.getMsg());
    }
 
    @Override
    public OAuth2Token token(OAuth2User user, OAuth2Request request) {
        // 移除注册后返回的令牌与刷新令牌,防止外部攻击采用注册接口获取令牌并调用低权接口
        // 注意:
        // 1. 框架已默认开启严格模式,blade.secure.strict-token=true,不移除令牌则不受影响,注册令牌会被框架校验并拒绝
        // 2. 若自行关闭严格模式,blade.secure.strict-token=false,必须将令牌移除,否则注册获取令牌后可调用低权接口
        OAuth2Token token = super.token(user, request);
        token.getArgs().remove(TokenConstant.ACCESS_TOKEN);
        token.getArgs().remove(TokenConstant.REFRESH_TOKEN);
        return token;
    }
 
    private void validateUser(User user) {
        Predicate<String> isNameValid = name -> name.matches("^([\\u4e00-\\u9fa5]{2,20}|[a-zA-Z]{2,10})$");
        Predicate<String> isUsernameValid = username -> username.matches("^(?=.*[a-zA-Z])[a-zA-Z0-9_\\-@]{3,20}$");
        Predicate<String> isPasswordValid = password -> password.matches("^(?=.*[0-9])(?=.*[a-zA-Z])[\\w@-]{6,20}$");
        Predicate<String> isPhoneValid = phone -> phone.matches("^1[3-9]\\d{9}$");
        Predicate<String> isEmailValid = email -> email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
        if (!isNameValid.test(user.getName())) {
            throw new UserInvalidException("用户姓名长度必须在2-10之间,且仅能设置纯中文或纯英文");
        }
        if (!isUsernameValid.test(user.getAccount())) {
            throw new UserInvalidException("用户账号长度必须在3-20之间,且需要包含英文,可额外携带数字、下划线、横杠、@");
        }
        if (!isPasswordValid.test(user.getPassword())) {
            throw new UserInvalidException("用户密码长度必须在6-20之间,且需要包含英文与数字,可额外携带下划线、横杠、@");
        }
        if (!isPhoneValid.test(user.getPhone())) {
            throw new UserInvalidException("手机号格式不正确");
        }
        if (!isEmailValid.test(user.getEmail())) {
            throw new UserInvalidException("邮箱格式不正确");
        }
    }
 
    @NotNull
    private OAuth2UserDetail convertOAuth2UserDetail(User user, OAuth2Client client) {
        OAuth2UserDetail userDetail = new OAuth2UserDetail();
        userDetail.setUserId(String.valueOf(user.getId()));
        userDetail.setTenantId(user.getTenantId());
        userDetail.setName(user.getName());
        userDetail.setRealName(user.getName());
        userDetail.setAccount(user.getAccount());
        userDetail.setPassword(user.getPassword());
        userDetail.setPhone(user.getPhone());
        userDetail.setEmail(user.getEmail());
        userDetail.setAuthorities(Collections.singletonList(REGISTER));
        userDetail.setClient(client);
        return userDetail;
    }
}