修改默认登录页
This commit is contained in:
parent
2fb93f2b5d
commit
d053df88d1
|
|
@ -5,7 +5,10 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b713637a-3b19-4c5b-9e88-95e35dd83d2e" name="更改" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/../oidc/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../oidc/pom.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/../oidc/src/main/java/com/tuoheng/oauth/oidc/config/SecurityConfig.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservice/target/classes/application.properties" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/HelloController.class" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/../resourceservice/target/classes/com/tuoheng/resourceservice/ResourceServiceApplication.class" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
|
@ -106,6 +109,7 @@
|
|||
<workItem from="1752745264222" duration="2832000" />
|
||||
<workItem from="1752751160098" duration="1320000" />
|
||||
<workItem from="1752798688493" duration="8426000" />
|
||||
<workItem from="1752886146802" duration="618000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
server.port=8080
|
||||
|
||||
spring.cloud.gateway.routes[0].id=resource-server-a
|
||||
spring.cloud.gateway.routes[0].uri=http://localhost:8081
|
||||
spring.cloud.gateway.routes[0].predicates[0]=Path=/a/**
|
||||
spring.cloud.gateway.routes[0].filters[0]=RewritePath=/a/(?<segment>.*), /api/${segment}
|
||||
spring.cloud.gateway.routes[0].filters[1]=TokenRelay
|
||||
|
||||
spring.cloud.gateway.routes[1].id=resource-server-b
|
||||
spring.cloud.gateway.routes[1].uri=http://localhost:8082
|
||||
spring.cloud.gateway.routes[1].predicates[0]=Path=/b/**
|
||||
spring.cloud.gateway.routes[1].filters[0]=RewritePath=/b/(?<segment>.*), /api/${segment}
|
||||
spring.cloud.gateway.routes[1].filters[1]=TokenRelay
|
||||
|
||||
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9000/oauth2/jwks
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -84,7 +84,13 @@ public class SecurityConfig {
|
|||
.anyRequest().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2 -> oauth2.jwt()) // 新增,支持JWT
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.loginProcessingUrl("/login")
|
||||
.defaultSuccessUrl("/")
|
||||
.failureUrl("/login?error=bad_credentials")
|
||||
.permitAll()
|
||||
)
|
||||
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // 添加CORS支持
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers("/logout")) // 禁用logout端点的CSRF保护
|
||||
.logout(logout -> logout
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tuoheng.oauth.oidc.controller;
|
||||
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@RestController
|
||||
public class LoginController {
|
||||
|
||||
@GetMapping("/login")
|
||||
@ResponseBody
|
||||
public String login(HttpServletRequest request) throws IOException {
|
||||
// 读取静态HTML文件
|
||||
String htmlContent = new String(Files.readAllBytes(Paths.get("src/main/resources/static/login.html")));
|
||||
|
||||
// 获取CSRF token
|
||||
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
|
||||
if (csrfToken != null) {
|
||||
// 替换CSRF token占位符
|
||||
htmlContent = htmlContent.replace("id=\"csrf-parameter\" name=\"\" value=\"\"",
|
||||
"id=\"csrf-parameter\" name=\"" + csrfToken.getParameterName() + "\" value=\"" + csrfToken.getToken() + "\"");
|
||||
}
|
||||
|
||||
return htmlContent;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OIDC 登录</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
padding: 40px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
color: #333;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group input[type="text"],
|
||||
.form-group input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e1e5e9;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
transition: border-color 0.3s ease;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.form-group input[type="text"]:focus,
|
||||
.form-group input[type="password"]:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.form-group input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.login-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #fee;
|
||||
border: 1px solid #fcc;
|
||||
color: #c33;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error-message.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<h1>OIDC 登录</h1>
|
||||
<p>请输入您的凭据以继续</p>
|
||||
</div>
|
||||
|
||||
<div id="error-message" class="error-message"></div>
|
||||
|
||||
<form id="login-form" method="post" action="/login">
|
||||
<input type="hidden" id="csrf-parameter" name="" value="" />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" id="username" name="username" required autocomplete="username">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">密码</label>
|
||||
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="remember-me" name="remember-me">
|
||||
<label for="remember-me">记住我</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-btn">登录</button>
|
||||
</form>
|
||||
|
||||
<div class="footer">
|
||||
<p>OIDC Authorization Server</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面加载时检查错误参数
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
// 检查是否有错误参数
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const error = urlParams.get('error');
|
||||
|
||||
if (error) {
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
errorMessage.textContent = getErrorMessage(error);
|
||||
errorMessage.classList.add('show');
|
||||
}
|
||||
});
|
||||
|
||||
// 表单提交处理
|
||||
document.getElementById('login-form').addEventListener('submit', function(e) {
|
||||
const username = document.getElementById('username').value.trim();
|
||||
const password = document.getElementById('password').value.trim();
|
||||
|
||||
if (!username || !password) {
|
||||
e.preventDefault();
|
||||
showError('请输入用户名和密码');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// 显示错误信息
|
||||
function showError(message) {
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
errorMessage.textContent = message;
|
||||
errorMessage.classList.add('show');
|
||||
}
|
||||
|
||||
// 根据错误类型返回对应的错误信息
|
||||
function getErrorMessage(error) {
|
||||
const errorMessages = {
|
||||
'bad_credentials': '用户名或密码错误',
|
||||
'account_locked': '账户已被锁定',
|
||||
'account_disabled': '账户已被禁用',
|
||||
'account_expired': '账户已过期',
|
||||
'credentials_expired': '密码已过期',
|
||||
'session_authentication_exception': '会话认证失败',
|
||||
'default': '登录失败,请重试'
|
||||
};
|
||||
|
||||
return errorMessages[error] || errorMessages['default'];
|
||||
}
|
||||
|
||||
// 输入框获得焦点时隐藏错误信息
|
||||
document.getElementById('username').addEventListener('focus', function() {
|
||||
document.getElementById('error-message').classList.remove('show');
|
||||
});
|
||||
|
||||
document.getElementById('password').addEventListener('focus', function() {
|
||||
document.getElementById('error-message').classList.remove('show');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1 +0,0 @@
|
|||
server.port=8081
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
server.port=8082
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue