add HttpTrustEntryPoint

This commit is contained in:
shimingxy 2024-12-02 09:01:27 +08:00
parent 167ea5da82
commit 6688db78bb
18 changed files with 277 additions and 155 deletions

View File

@ -282,6 +282,8 @@ subprojects {
//oauth third party JustAuth
implementation group: 'com.xkcoding.http', name: 'simple-http', version: "${simplehttpVersion}"
implementation group: 'me.zhyd.oauth', name: 'JustAuth', version: "${JustAuthVersion}"
//cas
implementation group: 'org.apereo.cas.client', name: 'cas-client-core', version: "${casclientVersion}"
//common
implementation group: 'org.javassist', name: 'javassist', version: "${javassistVersion}"
implementation group: 'org.owasp.esapi', name: 'esapi', version: "${esapiVersion}"

View File

@ -199,6 +199,7 @@ prometheusVersion =0.16.0
mapstructVersion =1.5.3.Final
JustAuthVersion =1.16.5
simplehttpVersion =1.0.5
casclientVersion =4.0.4
#doc
swaggerV3Version =2.2.21
classgraphVersion =4.8.149

View File

@ -0,0 +1,58 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.maxkey.authn.support.cas;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.Cas20ServiceTicketValidator;
import org.apereo.cas.client.validation.TicketValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CasTrustLoginService {
private static final Logger _logger = LoggerFactory.getLogger(CasTrustLoginService.class);
String service;
Cas20ServiceTicketValidator cas20ServiceTicketValidator;
public CasTrustLoginService(String casServerUrlPrefix,String service) {
this.service = service;
this.cas20ServiceTicketValidator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
}
public String buildLoginUser(String ticket) {
_logger.debug("build Login User .");
String user = null;
Assertion assertion;
try {
assertion = cas20ServiceTicketValidator.validate(ticket, service);
if(assertion != null) {
user = assertion.getPrincipal().getName();
}
} catch (TicketValidationException e) {
_logger.error("cas TicketValidationException" , e);
e.printStackTrace();
}
_logger.debug("cas user : {}" , user);
return user;
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.maxkey.authn.support.cas;
import org.dromara.maxkey.authn.LoginCredential;
import org.dromara.maxkey.authn.jwt.AuthJwt;
import org.dromara.maxkey.authn.jwt.AuthTokenService;
import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider;
import org.dromara.maxkey.configuration.ApplicationConfig;
import org.dromara.maxkey.constants.ConstsLoginType;
import org.dromara.maxkey.entity.Message;
import org.dromara.maxkey.web.WebConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/login")
public class HttpTrustEntryPoint {
private static final Logger _logger = LoggerFactory.getLogger(HttpTrustEntryPoint.class);
@Autowired
ApplicationConfig applicationConfig;
@Autowired
AbstractAuthenticationProvider authenticationProvider ;
@Autowired
AuthTokenService authTokenService;
@Autowired
CasTrustLoginService casTrustLoginService;
@GetMapping(value={"/trust"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public Message<AuthJwt> trust(@RequestParam(value = WebConstants.CAS_TICKET_PARAMETER, required = true) String ticket) {
try {
//for ticket Login
_logger.debug("ticket : {}" , ticket);
String username = casTrustLoginService.buildLoginUser(ticket);
if(username != null) {
LoginCredential loginCredential =new LoginCredential(username,"",ConstsLoginType.CAS);
Authentication authentication = authenticationProvider.authenticate(loginCredential,true);
_logger.debug("CAS Logined in , username {}" , username);
AuthJwt authJwt = authTokenService.genAuthJwt(authentication);
return new Message<>(authJwt);
}
}catch(Exception e) {
_logger.error("Exception ",e);
}
return new Message<>(Message.FAIL);
}
public void setApplicationConfig(ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
}
public void setAuthenticationProvider(AbstractAuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
}

View File

@ -1,119 +0,0 @@
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.maxkey.authn.support.jwt;
import org.dromara.maxkey.authn.LoginCredential;
import org.dromara.maxkey.authn.jwt.AuthJwt;
import org.dromara.maxkey.authn.jwt.AuthTokenService;
import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider;
import org.dromara.maxkey.configuration.ApplicationConfig;
import org.dromara.maxkey.constants.ConstsLoginType;
import org.dromara.maxkey.entity.Message;
import org.dromara.maxkey.web.WebConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.nimbusds.jwt.SignedJWT;
@RestController
@RequestMapping(value = "/login")
public class HttpJwtEntryPoint {
private static final Logger _logger = LoggerFactory.getLogger(HttpJwtEntryPoint.class);
@Autowired
ApplicationConfig applicationConfig;
@Autowired
AbstractAuthenticationProvider authenticationProvider ;
@Autowired
AuthTokenService authTokenService;
@Autowired
JwtLoginService jwtLoginService;
@RequestMapping(value={"/jwt"}, produces = {MediaType.APPLICATION_JSON_VALUE},method={RequestMethod.GET,RequestMethod.POST})
public Message<AuthJwt> jwt(@RequestParam(value = WebConstants.JWT_TOKEN_PARAMETER, required = true) String jwt) {
try {
//for jwt Login
_logger.debug("jwt : {}" , jwt);
SignedJWT signedJWT = jwtLoginService.jwtTokenValidation(jwt);
if(signedJWT != null) {
String username =signedJWT.getJWTClaimsSet().getSubject();
LoginCredential loginCredential =new LoginCredential(username,"",ConstsLoginType.JWT);
Authentication authentication = authenticationProvider.authenticate(loginCredential,true);
_logger.debug("JWT Logined in , username {}" , username);
AuthJwt authJwt = authTokenService.genAuthJwt(authentication);
return new Message<>(authJwt);
}
}catch(Exception e) {
_logger.error("Exception ",e);
}
return new Message<>(Message.FAIL);
}
/**
* trust same HS512
* @param jwt
* @return
*/
@RequestMapping(value={"/jwt/trust"}, produces = {MediaType.APPLICATION_JSON_VALUE},method={RequestMethod.GET,RequestMethod.POST})
public Message<AuthJwt> jwtTrust(@RequestParam(value = WebConstants.JWT_TOKEN_PARAMETER, required = true) String jwt) {
try {
//for jwt Login
_logger.debug("jwt : {}" , jwt);
if(authTokenService.validateJwtToken(jwt)) {
String username =authTokenService.resolve(jwt).getSubject();
LoginCredential loginCredential =new LoginCredential(username,"",ConstsLoginType.JWT);
Authentication authentication = authenticationProvider.authenticate(loginCredential,true);
_logger.debug("JWT Logined in , username {}" , username);
AuthJwt authJwt = authTokenService.genAuthJwt(authentication);
return new Message<>(authJwt);
}
}catch(Exception e) {
_logger.error("Exception ",e);
}
return new Message<>(Message.FAIL);
}
public void setApplicationConfig(ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
}
public void setAuthenticationProvider(AbstractAuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
public void setJwtLoginService(JwtLoginService jwtLoginService) {
this.jwtLoginService = jwtLoginService;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.maxkey.autoconfigure;
import org.dromara.maxkey.authn.support.cas.CasTrustLoginService;
import org.dromara.maxkey.configuration.LoginConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
public class CasAuthnAutoConfiguration {
private static final Logger _logger = LoggerFactory.getLogger(CasAuthnAutoConfiguration.class);
/**
* CAS LoginService.
* @return
*/
@Bean
CasTrustLoginService casTrustLoginService(LoginConfig loginConfig) {
CasTrustLoginService casTrustLoginService = new CasTrustLoginService(
loginConfig.getCasServerUrlPrefix() ,
loginConfig.getCasService());
_logger.debug("CAS Login Service init.");
return casTrustLoginService;
}
}

View File

@ -1,2 +1,2 @@
org.dromara.maxkey.autoconfigure.AuthnProviderAutoConfiguration
org.dromara.maxkey.autoconfigure.JwtAuthnAutoConfiguration
org.dromara.maxkey.autoconfigure.CasAuthnAutoConfiguration

View File

@ -18,6 +18,7 @@
package org.dromara.maxkey.configuration;
import org.dromara.maxkey.constants.ConstsDatabase;
import org.dromara.maxkey.constants.ConstsPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@ -67,6 +68,9 @@ public class ApplicationConfig {
@Value("${maxkey.server.provision:false}")
private boolean provision;
@Value("${maxkey.server.persistence}")
int persistence;
@Value("${maxkey.notices.visible:false}")
private boolean noticesVisible;
@ -201,6 +205,22 @@ public class ApplicationConfig {
return provision;
}
public int getPersistence() {
return persistence;
}
public void setPersistence(int persistence) {
this.persistence = persistence;
}
public boolean isPersistenceRedis() {
return persistence == ConstsPersistence.REDIS;
}
public boolean isPersistenceInmemory() {
return persistence == ConstsPersistence.INMEMORY;
}
public String getMgtUri() {
return mgtUri;
}

View File

@ -37,6 +37,12 @@ public class LoginConfig {
@Value("${maxkey.login.wsfederation}")
boolean wsFederation;
@Value("${maxkey.login.cas.serverUrlPrefix:http://sso.maxkey.top/sign/authz/cas}")
String casServerUrlPrefix;
@Value("${maxkey.login.cas.service:http://mgt.maxkey.top/maxkey-mgt/passport/trust/auth}")
String casService;
/**
* .
@ -86,7 +92,23 @@ public class LoginConfig {
this.wsFederation = wsFederation;
}
@Override
public String getCasServerUrlPrefix() {
return casServerUrlPrefix;
}
public void setCasServerUrlPrefix(String casServerUrlPrefix) {
this.casServerUrlPrefix = casServerUrlPrefix;
}
public String getCasService() {
return casService;
}
public void setCasService(String casService) {
this.casService = casService;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("LoginConfig [mfa=");

View File

@ -56,6 +56,8 @@ public class WebConstants {
public static final String REMEBER_ME_COOKIE = "sign_remeber_me";
public static final String JWT_TOKEN_PARAMETER = "jwt";
public static final String CAS_TICKET_PARAMETER = "ticket";
public static final String CURRENT_SINGLESIGNON_URI = "current_singlesignon_uri";

View File

@ -5,10 +5,7 @@ description = "maxkey-protocol-cas"
dependencies {
//local jars
implementation fileTree(dir: '../maxkey-lib/*/', include: '*.jar')
// https://mvnrepository.com/artifact/org.jasig.cas.client/cas-client-core
testImplementation group: 'org.jasig.cas.client', name: 'cas-client-core', version: '3.6.1'
testImplementation group: 'org.pac4j', name: 'pac4j-core', version: '3.8.3'
// https://mvnrepository.com/artifact/org.pac4j/pac4j-cas
testImplementation group: 'org.pac4j', name: 'pac4j-cas', version: '3.8.3'

View File

@ -37,6 +37,6 @@ export class AuthzMgtComponent implements OnInit {
} else {
baseUrl = environment.api.baseUrl;
}
window.location.href = `${baseUrl}/authz/jwt/maxkey_mgt`;
window.location.href = `${baseUrl}/authz/maxkey_mgt`;
}
}

View File

@ -50,7 +50,7 @@ import { Console } from 'console';
styleUrls: ['./home.component.less']
})
export class HomeComponent implements OnInit {
loading: boolean = false;
loading: boolean = true;
appList: any[] = [];
baseUrl: String = '';
staticAppList: any[] = [];
@ -114,6 +114,7 @@ export class HomeComponent implements OnInit {
//console.log(res.data);
this.appList = res.data;
this.staticAppList = this.appList;
this.loading = false;
this.cdr.detectChanges();
});
}

View File

@ -19,11 +19,11 @@ import { RouterModule, Routes } from '@angular/router';
import { LayoutPassportComponent } from '../../layout/passport/passport.component';
import { CallbackComponent } from './callback.component';
import { JwtAuthComponent } from './jwt-auth.component';
import { UserLockComponent } from './lock/lock.component';
import { UserLoginComponent } from './login/login.component';
import { UserRegisterResultComponent } from './register-result/register-result.component';
import { UserRegisterComponent } from './register/register.component';
import { TrustAuthComponent } from './trust-auth.component';
const routes: Routes = [
// passport
@ -54,7 +54,7 @@ const routes: Routes = [
]
},
// 单页不包裹Layout
{ path: 'passport/jwt/auth', component: JwtAuthComponent }
{ path: 'passport/trust/auth', component: TrustAuthComponent }
];
@NgModule({

View File

@ -23,11 +23,11 @@ import { SettingsService } from '@delon/theme';
import { AuthnService } from '../../service/authn.service';
@Component({
selector: 'app-jwt-auth',
selector: 'app-trust-auth',
template: ``
})
export class JwtAuthComponent implements OnInit {
jwt = '';
export class TrustAuthComponent implements OnInit {
ticket = '';
constructor(
private authnService: AuthnService,
@ -39,9 +39,9 @@ export class JwtAuthComponent implements OnInit {
) {}
ngOnInit(): void {
this.jwt = this.route.snapshot.queryParams['jwt'];
this.ticket = this.route.snapshot.queryParams['ticket'];
this.authnService.jwtAuth({ jwt: this.jwt }).subscribe(res => {
this.authnService.trustAuth({ ticket: this.ticket }).subscribe(res => {
if (res.code !== 0) {
this.router.navigateByUrl('/passport/login');
} else {

View File

@ -61,8 +61,8 @@ export class AuthnService {
return this.http.post('/login/congress?_allow_anonymous=true', authParam);
}
jwtAuth(authParam: any) {
return this.http.get(`/login/jwt?_allow_anonymous=true`, authParam);
trustAuth(authParam: any) {
return this.http.get(`/login/trust?_allow_anonymous=true`, authParam);
}
logout() {

View File

@ -26,6 +26,7 @@
<title id="maxkey_title" name="description">MaxKey-业界领先的IAM身份管理和认证产品</title>
<meta http-equiv="description" content="MaxKey Single Sign-On">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<script src="./assets/transform.js"></script>
<!-- Apple Touch Icon -->
<!-- <link rel="apple-touch-icon" href="custom-icon.png"> -->
<style type="text/css">.preloader{position:fixed;top:0;left:0;z-index:9999;width:100%;height:100%;overflow:hidden;background:#49a9ee;transition:opacity .65s}.preloader-hidden-add{display:block;opacity:1}.preloader-hidden-add-active{opacity:0}.preloader-hidden{display:none}.cs-loader{position:absolute;top:0;left:0;width:100%;height:100%}.cs-loader-inner{position:absolute;top:50%;width:100%;color:#fff;text-align:center;transform:translateY(-50%)}.cs-loader-inner label{display:inline-block;font-size:20px;opacity:0}@keyframes lol{0%{transform:translateX(-300px);opacity:0}33%{transform:translateX(0);opacity:1}66%{transform:translateX(0);opacity:1}100%{transform:translateX(300px);opacity:0}}.cs-loader-inner label:nth-child(6){animation:lol 3s infinite ease-in-out}.cs-loader-inner label:nth-child(5){animation:lol 3s .1s infinite ease-in-out}.cs-loader-inner label:nth-child(4){animation:lol 3s .2s infinite ease-in-out}.cs-loader-inner label:nth-child(3){animation:lol 3s .3s infinite ease-in-out}.cs-loader-inner label:nth-child(2){animation:lol 3s .4s infinite ease-in-out}.cs-loader-inner label:nth-child(1){animation:lol 3s .5s infinite ease-in-out}</style>

View File

@ -24,10 +24,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.dromara.maxkey.authn.listener.SessionListenerAdapter;
import org.dromara.maxkey.authn.realm.jdbc.JdbcAuthenticationRealm;
import org.dromara.maxkey.authn.realm.ldap.LdapAuthenticationRealmService;
import org.dromara.maxkey.authn.session.Session.CATEGORY;
import org.dromara.maxkey.authn.session.SessionManager;
import org.dromara.maxkey.authn.support.kerberos.KerberosProxy;
import org.dromara.maxkey.authn.support.kerberos.RemoteKerberosService;
import org.dromara.maxkey.configuration.ApplicationConfig;
import org.dromara.maxkey.configuration.EmailConfig;
import org.dromara.maxkey.constants.ConstsPersistence;
import org.dromara.maxkey.ip2location.IpLocationParser;
@ -43,37 +47,21 @@ import org.dromara.maxkey.persistence.repository.LoginRepository;
import org.dromara.maxkey.persistence.repository.PasswordPolicyValidator;
import org.dromara.maxkey.persistence.service.CnfLdapContextService;
import org.dromara.maxkey.persistence.service.UserInfoService;
import org.dromara.maxkey.schedule.ScheduleAdapterBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
@AutoConfiguration
@ComponentScan(basePackages = {
"org.maxkey.authn",
"org.maxkey.configuration",
"org.maxkey.domain",
"org.maxkey.domain.apps",
"org.maxkey.domain.userinfo",
"org.maxkey.api.v1.contorller",
"org.maxkey.web.endpoint",
"org.maxkey.web.contorller",
"org.maxkey.web.interceptor",
//single sign on protocol
"org.maxkey.authz.endpoint",
"org.maxkey.authz.desktop.endpoint",
"org.maxkey.authz.exapi.endpoint",
"org.maxkey.authz.formbased.endpoint",
"org.maxkey.authz.ltpa.endpoint",
"org.maxkey.authz.token.endpoint"
})
public class MaxKeyConfig {
private static final Logger logger = LoggerFactory.getLogger(MaxKeyConfig.class);
@ -213,4 +201,22 @@ public class MaxKeyConfig {
return kerberosService;
}
@Bean
String sessionListenerAdapter(
Scheduler scheduler,
ApplicationConfig applicationConfig,
SessionManager sessionManager) throws SchedulerException {
if(applicationConfig.isPersistenceInmemory()) {
new ScheduleAdapterBuilder()
.setScheduler(scheduler)
.setCron("0 0/10 * * * ?")
.setJobClass(SessionListenerAdapter.class)
.setJobData("sessionManager",sessionManager)
.setJobData("category", CATEGORY.SIGN)
.build();
logger.debug("Session ListenerAdapter inited .");
}
return "sessionListenerAdapter";
}
}