mirror of
https://github.com/casdoor/casdoor.git
synced 2025-09-06 01:40:27 +08:00
feat: support inline-captcha in login page (#3970)
This commit is contained in:
@@ -437,18 +437,26 @@ class LoginPage extends React.Component {
|
|||||||
values["password"] = passwordCipher;
|
values["password"] = passwordCipher;
|
||||||
}
|
}
|
||||||
const captchaRule = this.getCaptchaRule(this.getApplicationObj());
|
const captchaRule = this.getCaptchaRule(this.getApplicationObj());
|
||||||
if (captchaRule === CaptchaRule.Always) {
|
const application = this.getApplicationObj();
|
||||||
this.setState({
|
const noModal = application?.signinItems.map(signinItem => signinItem.name === "Captcha" && signinItem.rule === "inline").includes(true);
|
||||||
openCaptchaModal: true,
|
if (!noModal) {
|
||||||
values: values,
|
if (captchaRule === CaptchaRule.Always) {
|
||||||
});
|
this.setState({
|
||||||
return;
|
openCaptchaModal: true,
|
||||||
} else if (captchaRule === CaptchaRule.Dynamic) {
|
values: values,
|
||||||
this.checkCaptchaStatus(values);
|
});
|
||||||
return;
|
return;
|
||||||
} else if (captchaRule === CaptchaRule.InternetOnly) {
|
} else if (captchaRule === CaptchaRule.Dynamic) {
|
||||||
this.checkCaptchaStatus(values);
|
this.checkCaptchaStatus(values);
|
||||||
return;
|
return;
|
||||||
|
} else if (captchaRule === CaptchaRule.InternetOnly) {
|
||||||
|
this.checkCaptchaStatus(values);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values["captchaType"] = this.state?.captchaValues?.captchaType;
|
||||||
|
values["captchaToken"] = this.state?.captchaValues?.captchaToken;
|
||||||
|
values["clientSecret"] = this.state?.captchaValues?.clientSecret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.login(values);
|
this.login(values);
|
||||||
@@ -775,7 +783,7 @@ class LoginPage extends React.Component {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.renderCaptchaModal(application)
|
application?.signinItems.map(signinItem => signinItem.name === "Captcha" && signinItem.rule === "inline").includes(true) ? null : this.renderCaptchaModal(application, false)
|
||||||
}
|
}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
@@ -819,6 +827,8 @@ class LoginPage extends React.Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (signinItem.name === "Captcha" && signinItem.rule === "inline") {
|
||||||
|
return this.renderCaptchaModal(application, true);
|
||||||
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
|
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
|
||||||
return (
|
return (
|
||||||
<div key={resultItemKey} dangerouslySetInnerHTML={{__html: signinItem.customCss}} />
|
<div key={resultItemKey} dangerouslySetInnerHTML={{__html: signinItem.customCss}} />
|
||||||
@@ -964,7 +974,7 @@ class LoginPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCaptchaModal(application) {
|
renderCaptchaModal(application, noModal) {
|
||||||
if (this.getCaptchaRule(this.getApplicationObj()) === CaptchaRule.Never) {
|
if (this.getCaptchaRule(this.getApplicationObj()) === CaptchaRule.Never) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -993,6 +1003,12 @@ class LoginPage extends React.Component {
|
|||||||
owner={provider.owner}
|
owner={provider.owner}
|
||||||
name={provider.name}
|
name={provider.name}
|
||||||
visible={this.state.openCaptchaModal}
|
visible={this.state.openCaptchaModal}
|
||||||
|
noModal={noModal}
|
||||||
|
onUpdateToken={(captchaType, captchaToken, clientSecret) => {
|
||||||
|
this.setState({captchaValues: {
|
||||||
|
captchaType, captchaToken, clientSecret,
|
||||||
|
}});
|
||||||
|
}}
|
||||||
onOk={(captchaType, captchaToken, clientSecret) => {
|
onOk={(captchaType, captchaToken, clientSecret) => {
|
||||||
const values = this.state.values;
|
const values = this.state.values;
|
||||||
values["captchaType"] = captchaType;
|
values["captchaType"] = captchaType;
|
||||||
|
@@ -20,7 +20,7 @@ import {CaptchaWidget} from "../CaptchaWidget";
|
|||||||
import {SafetyOutlined} from "@ant-design/icons";
|
import {SafetyOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
export const CaptchaModal = (props) => {
|
export const CaptchaModal = (props) => {
|
||||||
const {owner, name, visible, onOk, onCancel, isCurrentProvider} = props;
|
const {owner, name, visible, onOk, onUpdateToken, onCancel, isCurrentProvider, noModal} = props;
|
||||||
|
|
||||||
const [captchaType, setCaptchaType] = React.useState("none");
|
const [captchaType, setCaptchaType] = React.useState("none");
|
||||||
const [clientId, setClientId] = React.useState("");
|
const [clientId, setClientId] = React.useState("");
|
||||||
@@ -36,16 +36,16 @@ export const CaptchaModal = (props) => {
|
|||||||
const defaultInputRef = React.useRef(null);
|
const defaultInputRef = React.useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible || noModal) {
|
||||||
loadCaptcha();
|
loadCaptcha();
|
||||||
} else {
|
} else {
|
||||||
handleCancel();
|
handleCancel();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
}, [visible]);
|
}, [visible, noModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (captchaToken !== "" && captchaType !== "Default") {
|
if (captchaToken !== "" && captchaType !== "Default" && !noModal) {
|
||||||
handleOk();
|
handleOk();
|
||||||
}
|
}
|
||||||
}, [captchaToken]);
|
}, [captchaToken]);
|
||||||
@@ -81,6 +81,36 @@ export const CaptchaModal = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderDefaultCaptcha = () => {
|
const renderDefaultCaptcha = () => {
|
||||||
|
if (noModal) {
|
||||||
|
return (
|
||||||
|
<Row style={{textAlign: "center"}}>
|
||||||
|
<Col
|
||||||
|
style={{flex: noModal ? "70%" : "100%"}}>
|
||||||
|
<Input
|
||||||
|
ref={defaultInputRef}
|
||||||
|
value={captchaToken}
|
||||||
|
prefix={<SafetyOutlined />}
|
||||||
|
placeholder={i18next.t("general:Captcha")}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col
|
||||||
|
style={{
|
||||||
|
flex: noModal ? "30%" : "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={`data:image/png;base64,${captchaImg}`}
|
||||||
|
onClick={loadCaptcha}
|
||||||
|
style={{
|
||||||
|
borderRadius: "5px",
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
marginBottom: "20px",
|
||||||
|
width: "100%",
|
||||||
|
}} alt="captcha" />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Col style={{textAlign: "center"}}>
|
<Col style={{textAlign: "center"}}>
|
||||||
<div style={{display: "inline-block"}}>
|
<div style={{display: "inline-block"}}>
|
||||||
@@ -113,6 +143,9 @@ export const CaptchaModal = (props) => {
|
|||||||
|
|
||||||
const onChange = (token) => {
|
const onChange = (token) => {
|
||||||
setCaptchaToken(token);
|
setCaptchaToken(token);
|
||||||
|
if (noModal) {
|
||||||
|
onUpdateToken?.(captchaType, token, clientSecret);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderCaptcha = () => {
|
const renderCaptcha = () => {
|
||||||
@@ -153,28 +186,33 @@ export const CaptchaModal = (props) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
if (noModal) {
|
||||||
<Modal
|
return renderCaptcha();
|
||||||
closable={true}
|
|
||||||
maskClosable={false}
|
} else {
|
||||||
destroyOnClose={true}
|
return (
|
||||||
title={i18next.t("general:Captcha")}
|
<Modal
|
||||||
open={open}
|
closable={true}
|
||||||
okText={i18next.t("general:OK")}
|
maskClosable={false}
|
||||||
cancelText={i18next.t("general:Cancel")}
|
destroyOnClose={true}
|
||||||
width={350}
|
title={i18next.t("general:Captcha")}
|
||||||
footer={renderFooter()}
|
open={open}
|
||||||
onCancel={handleCancel}
|
okText={i18next.t("general:OK")}
|
||||||
afterClose={handleCancel}
|
cancelText={i18next.t("general:Cancel")}
|
||||||
onOk={handleOk}
|
width={350}
|
||||||
>
|
footer={renderFooter()}
|
||||||
<div style={{marginTop: "20px", marginBottom: "50px"}}>
|
onCancel={handleCancel}
|
||||||
{
|
afterClose={handleCancel}
|
||||||
renderCaptcha()
|
onOk={handleOk}
|
||||||
}
|
>
|
||||||
</div>
|
<div style={{marginTop: "20px", marginBottom: "50px"}}>
|
||||||
</Modal>
|
{
|
||||||
);
|
renderCaptcha()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CaptchaRule = {
|
export const CaptchaRule = {
|
||||||
|
@@ -49,6 +49,9 @@ class SigninTable extends React.Component {
|
|||||||
|
|
||||||
updateField(table, index, key, value) {
|
updateField(table, index, key, value) {
|
||||||
table[index][key] = value;
|
table[index][key] = value;
|
||||||
|
if (key === "name" && value === "Captcha") {
|
||||||
|
table[index]["rule"] = "pop up";
|
||||||
|
}
|
||||||
this.updateTable(table);
|
this.updateTable(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +117,7 @@ class SigninTable extends React.Component {
|
|||||||
{name: "Forgot password?", displayName: i18next.t("login:Forgot password?")},
|
{name: "Forgot password?", displayName: i18next.t("login:Forgot password?")},
|
||||||
{name: "Login button", displayName: i18next.t("login:Signin button")},
|
{name: "Login button", displayName: i18next.t("login:Signin button")},
|
||||||
{name: "Signup link", displayName: i18next.t("general:Signup link")},
|
{name: "Signup link", displayName: i18next.t("general:Signup link")},
|
||||||
|
{name: "Captcha", displayName: i18next.t("general:Captcha")},
|
||||||
];
|
];
|
||||||
|
|
||||||
const getItemDisplayName = (text) => {
|
const getItemDisplayName = (text) => {
|
||||||
@@ -249,6 +253,12 @@ class SigninTable extends React.Component {
|
|||||||
{id: "small", name: i18next.t("application:Small icon")},
|
{id: "small", name: i18next.t("application:Small icon")},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
if (record.name === "Captcha") {
|
||||||
|
options = [
|
||||||
|
{id: "pop up", name: i18next.t("application:Pop up")},
|
||||||
|
{id: "inline", name: i18next.t("application:Inline")},
|
||||||
|
];
|
||||||
|
}
|
||||||
if (options.length === 0) {
|
if (options.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user