user accout

This commit is contained in:
MaxKey 2022-05-03 11:23:52 +08:00
parent 95231504bb
commit 3e080d568f
49 changed files with 487 additions and 788 deletions

View File

@ -19,13 +19,9 @@ package org.maxkey.web.contorller;
import com.google.code.kaptcha.Producer;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.maxkey.authn.jwt.AuthTokenService;
import org.maxkey.crypto.Base64Utils;
import org.maxkey.entity.Message;
import org.maxkey.persistence.MomentaryService;
import org.slf4j.Logger;
@ -94,16 +90,10 @@ public class ImageCaptchaEndpoint {
momentaryService.put("", kaptchaKey, kaptchaValue);
// create the image with the text
BufferedImage bufferedImage = captchaProducer.createImage(kaptchaText);
// write the data out
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", stream);
String b64Image = "data:image/png;base64," +
Base64.getEncoder().encodeToString(stream.toByteArray());
String b64Image = Base64Utils.encodeImage(bufferedImage);
_logger.trace("b64Image {}" , b64Image);
stream.close();
return new Message<ImageCaptcha>(
new ImageCaptcha(state,b64Image)
).buildResponse();

View File

@ -17,9 +17,14 @@
package org.maxkey.crypto;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.imageio.ImageIO;
import org.apache.commons.codec.binary.Base64;
/**
@ -56,6 +61,25 @@ public final class Base64Utils {
return decoderBase64(cipher);
}
public static String encodeImage(BufferedImage bufferedImage) {
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", stream);
String b64Image = "data:image/png;base64," +
java.util.Base64.getEncoder().encodeToString(stream.toByteArray());
stream.close();
return b64Image;
}catch (Exception e) {
e.printStackTrace();
}
return "";
}
public static String encodeImage(byte[] byteImage) {
return "data:image/png;base64," +
java.util.Base64.getEncoder().encodeToString(byteImage);
}
/**
* encode file to base64 Code String.
*

View File

@ -18,7 +18,6 @@
package org.maxkey.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
@ -29,6 +28,7 @@ import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
import org.maxkey.crypto.Base64Utils;
import org.maxkey.util.StringUtils;
/**
@ -421,8 +421,7 @@ public class UserInfo extends JpaBaseEntity {
public void transPictureBase64() {
if(picture != null) {
this.pictureBase64 = "data:image/png;base64," +
Base64.getEncoder().encodeToString(picture);
this.pictureBase64 = Base64Utils.encodeImage(picture);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* 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.
@ -18,7 +18,6 @@
package org.maxkey.entity.apps;
import java.io.Serializable;
import java.util.Base64;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -29,6 +28,7 @@ import javax.persistence.Table;
import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
import org.maxkey.constants.ConstsBoolean;
import org.maxkey.crypto.Base64Utils;
@Entity
@Table(name = "MXK_APPS")
@ -292,8 +292,7 @@ public class Apps extends JpaBaseEntity implements Serializable {
public void transIconBase64() {
if(icon !=null) {
this.iconBase64 = "data:image/png;base64," +
Base64.getEncoder().encodeToString(icon);
this.iconBase64 = Base64Utils.encodeImage(icon);
}
}

View File

@ -1,121 +0,0 @@
/*
* 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.maxkey.web.image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.maxkey.configuration.ApplicationConfig;
import org.maxkey.constants.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* AbstractImageEndpoint Producer Image .
* @author Crystal.Sea
*
*/
public class AbstractImageEndpoint {
private static final Logger _logger = LoggerFactory.getLogger(AbstractImageEndpoint.class);
@Autowired
@Qualifier("applicationConfig")
protected ApplicationConfig applicationConfig;
/**
* producerImage.
* @param request HttpServletRequest
* @param response HttpServletResponse
* @param bufferedImage BufferedImage
* @throws IOException error
*/
public static void producerImage(HttpServletRequest request,
HttpServletResponse response,
BufferedImage bufferedImage) throws IOException {
// Set to expire far in the past.
response.setDateHeader("Expires", 0);
// Set standard HTTP/1.1 no-cache headers.
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
// Set standard HTTP/1.0 no-cache header.
response.setHeader("Pragma", "no-cache");
// return a jpeg/gif
response.setContentType(ContentType.IMAGE_GIF);
_logger.trace("create the image");
// create the image
if (bufferedImage != null) {
ServletOutputStream out = response.getOutputStream();
// write the data out
ImageIO.write(bufferedImage, "gif", out);
try {
out.flush();
} finally {
out.close();
}
}
}
/**
* byte2BufferedImage.
* @param imageByte bytes
* @return
*/
public static BufferedImage byte2BufferedImage(byte[] imageByte) {
try {
InputStream in = new ByteArrayInputStream(imageByte);
BufferedImage bufferedImage = ImageIO.read(in);
return bufferedImage;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* bufferedImage2Byte.
* @param bufferedImage BufferedImage
* @return
*/
public static byte[] bufferedImage2Byte(BufferedImage bufferedImage) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "gif", byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void setApplicationConfig(ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.maxkey.web.image;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* ImageEndpoint Producer Image and captcha.
* @author Crystal.Sea
*
*/
@Controller
public class ImageEndpoint extends AbstractImageEndpoint {
private static final Logger _logger = LoggerFactory.getLogger(ImageEndpoint.class);
/**
* Session Image Producer.
*
* @param request HttpServletRequest
* @param response HttpServletResponse
*/
@RequestMapping("/image/{id}")
public void imageHandleRequest(HttpServletRequest request, HttpServletResponse response,
@PathVariable("id") String id) {
try {
// get session image bytes
byte[] image = (byte[]) request.getSession().getAttribute(id);
producerImage(request,response,byte2BufferedImage(image));
} catch (Exception e) {
_logger.error("captcha Producer Error " + e.getMessage());
}
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取当前页面地址标签
* <@currUrl/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("currUrl")
public class CurrUrlTagDirective implements TemplateDirectiveModel {
@Autowired
private HttpServletRequest request;
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
//String url = params.get(URL).toString();
String currUrl=request.getRequestURI();
env.getOut().append(currUrl);
}
}

View File

@ -1,65 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import org.maxkey.util.DateUtils;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取应用上下文标签
* <@date format="" value=""></@date>
* @author Crystal.Sea
*
*/
@FreemarkerTag("date")
public class DateTagDirective implements TemplateDirectiveModel {
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
String dateValue = params.get("value").toString();
String format = params.get("format").toString();
String dateString="";
if(dateValue==null) {
if(format==null) {
dateString=DateUtils.getCurrentDateAsString(DateUtils.FORMAT_DATE_YYYY_MM_DD);
}else {
dateString=DateUtils.getCurrentDateAsString(format);
}
}else {
if(format==null) {
dateString=DateUtils.format(DateUtils.tryParse(dateValue),DateUtils.FORMAT_DATE_YYYY_MM_DD);
}else {
dateString=DateUtils.format(DateUtils.tryParse(dateValue),format);
}
}
env.getOut().append(dateString);
}
}

View File

@ -1,48 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取应用上下文标签
* <@genId/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("genId")
public class GenIdTagDirective implements TemplateDirectiveModel {
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
env.getOut().append(UUID.randomUUID().toString().toLowerCase());
}
}

View File

@ -1,55 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取应用上下文标签
* <@parameter/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("parameter")
public class ParameterTagDirective implements TemplateDirectiveModel {
@Autowired
private HttpServletRequest request;
private String name;
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
name=params.get("name").toString();
env.getOut().append(request.getParameter(name));
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取应用上下文标签
* <@pathVar/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("pathVar")
public class PathVarTagDirective implements TemplateDirectiveModel {
@Autowired
private HttpServletRequest request;
private int index;
String pathVariable;
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
index=Integer.parseInt(params.get("index").toString());
String[] pathVariables=request.getAttribute(org.springframework.web.util.WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE).toString().split("/");
if(pathVariables==null){
pathVariables=request.getRequestURI().split("/");
}
if(index==0){
pathVariable=pathVariables[pathVariables.length-1];
}else{
pathVariable=pathVariables[index+1];
}
env.getOut().append(request.getParameter(pathVariable));
}
}

View File

@ -1,66 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 获取应用上下文标签
* <@locale/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("redirect")
public class RedirectTagDirective implements TemplateDirectiveModel {
@Autowired
private HttpServletRequest request;
private HttpServletResponse response;
private String basePath = null;
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
String location=params.get("url").toString();
basePath = request.getScheme()+"://"+request.getServerName();
int port=request.getServerPort();
//Ignore 443 or 80 port
if((port==443 && request.getScheme().equalsIgnoreCase("https"))
||(port==80 && request.getScheme().equalsIgnoreCase("http"))){
}else{
basePath += ":"+port;
}
basePath += request.getContextPath()+"";
response.sendRedirect(basePath+"/"+location);
}
}

View File

@ -1,86 +0,0 @@
/*
* 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.maxkey.web.tag;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* 静态变量读取
* <@static/>
* @author Crystal.Sea
*
*/
@FreemarkerTag("static")
public class StaticTagDirective implements TemplateDirectiveModel {
@Override
@SuppressWarnings("rawtypes")
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body)
throws TemplateException, IOException {
// 获取字符串变量
String[] c = params.get("name").toString().trim().split("@");
StringBuffer sb = new StringBuffer();
try {
if(null == c || c.length < 2) {
throw new TemplateException("至少应该包含一个@符。", env);
}
Class<?> clazz = null;
for(int i=0;i<c.length;i++) {
sb.append(c[i]).append("@");
if(i == 0) {
clazz = Class.forName(c[i]);
} else if(i != c.length - 1) {
Class<?>[] clazzs = clazz.getDeclaredClasses();
boolean flag = false;
for(Class<?> clz : clazzs) {
if(clz.getSimpleName().equals(c[i])) {
clazz = clz;
flag = true;
break;
}
}
if(!flag) {
throw new TemplateException("内部类 " + sb.substring(0, sb.length() - 1) + " 未找到。", env);
}
} else {
Field sp = clazz.getDeclaredField(c[i]);
env.getOut().write(sp.get(clazz).toString());
}
}
} catch (ClassNotFoundException e) {
throw new TemplateException("" + sb.substring(0, sb.length() - 1) + " 未找到。", e.getCause(), env);
} catch (NoSuchFieldException e) {
throw new TemplateException("属性 " + sb.substring(0, sb.length() - 1) + " 未找到。", e.getCause(), env);
} catch (IllegalAccessException e) {
throw new TemplateException("没权限访问 " + sb.substring(0, sb.length() - 1) + " 属性。", e.getCause(), env);
}
}
}

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
/* eslint-disable import/order */
/* eslint-disable import/no-duplicates */
import { HttpClientModule } from '@angular/common/http';

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Router } from '@angular/router';
@ -66,7 +65,8 @@ export class StartupService {
// 用户信息:包括姓名、头像、邮箱地址
//this.settingService.setUser(appData.user);
// ACL设置权限为全量
this.aclService.setFull(true);
this.aclService.setFull(false);
// 初始化菜单
this.menuService.add(appData.menu);
// 设置页面标题的后缀

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
/* eslint-disable import/order */
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { DelonACLModule } from '@delon/acl';

View File

@ -14,13 +14,14 @@
* limitations under the License.
*/
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ACLService } from '@delon/acl';
import { SettingsService, User } from '@delon/theme';
import { environment } from '@env/environment';
import { CONSTS } from 'src/app/shared/consts';
import { AuthnService } from '../../service/authn.service';
import { LayoutDefaultOptions } from '../../theme/layout-default';
@Component({

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Component, Inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';

View File

@ -0,0 +1,42 @@
/*
* 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.
*/
import { Inject, Optional, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ReuseTabService } from '@delon/abc/reuse-tab';
import { SettingsService } from '@delon/theme';
import { environment } from '@env/environment';
import { AuthnService } from '../../service/authn.service';
import { SocialsProviderService } from '../../service/socials-provider.service';
@Component({
selector: 'app-callback',
template: ``
})
export class AuthzMgtComponent implements OnInit {
constructor() { }
ngOnInit(): void {
let baseUrl = '';
if (environment.api.baseUrl.endsWith('/')) {
baseUrl = environment.api.baseUrl.substring(0, environment.api.baseUrl.length - 1);
} else {
baseUrl = environment.api.baseUrl;
}
window.location.href = `${baseUrl}/authz/jwt/maxkey_mgt`;
}
}

View File

@ -14,13 +14,13 @@
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SharedModule } from '@shared';
import { LayoutBlankComponent } from '../../layout/blank/blank.component';
import { AuthzMgtComponent } from './authz-mgt.component';
import { CredentialComponent } from './credential/credential.component';
import { Oauth2ApproveComponent } from './oauth2-approve/oauth2-approve.component';
const routes: Routes = [
@ -43,6 +43,11 @@ const routes: Routes = [
path: 'oauth2approve',
component: Oauth2ApproveComponent,
data: { title: 'credential', titleI18n: 'app.login.login' }
},
{
path: 'mgt',
component: AuthzMgtComponent,
data: { title: 'mgt', titleI18n: 'app.login.login' }
}
]
}

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, Inject, OnDestroy, Optional } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
@ -70,7 +69,9 @@ export class CredentialComponent implements OnInit {
if (res.code == 0) {
this.form.model.init(res.data);
this.msg.success(`提交成功`);
if (this.redirect_uri) {
window.location.href = `${environment.api.baseUrl}${this.redirect_uri}`;
}
} else {
this.msg.success(`提交失败`);
}

View File

@ -0,0 +1,106 @@
<form nz-form [formGroup]="formGroup" (ngSubmit)="onSubmit()" se-container="1">
<nz-form-item style="display: none">
<nz-form-label [nzMd]="6" nzFor="id">id</nz-form-label>
<nz-form-control [nzMd]="18" nzErrorTip="The input is not valid id!">
<input [(ngModel)]="form.model.id" [ngModelOptions]="{ standalone: true }" nz-input name="id" id="id" value="id" />
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label [nzSm]="6" [nzXs]="24" nzFor="displayName">{{ 'mxk.password.displayName' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="36" [nzXl]="48" nzErrorTip="The input is not displayName!">
<input
nz-input
disabled="true"
[(ngModel)]="form.model.displayName"
[ngModelOptions]="{ standalone: true }"
name="displayName"
id="displayName"
value="0"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label [nzSm]="6" [nzXs]="24" nzFor="username">{{ 'mxk.accounts.username' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid username!">
<input
nz-input
disabled="true"
[(ngModel)]="form.model.username"
[ngModelOptions]="{ standalone: true }"
name="username"
id="username"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label [nzSm]="6" [nzXs]="24" nzFor="appName">{{ 'mxk.accounts.appName' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid appName!">
<input
nz-input
disabled="true"
[(ngModel)]="form.model.appName"
[ngModelOptions]="{ standalone: true }"
name="appName"
id="appName"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label [nzSm]="6" [nzXs]="24" nzRequired nzFor="relatedUsername">{{ 'mxk.accounts.relatedUsername' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid relatedUsername!">
<input
nz-input
[(ngModel)]="form.model.relatedUsername"
[ngModelOptions]="{ standalone: true }"
name="relatedUsername"
id="relatedUsername"
/>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label nzRequired [nzSm]="6" [nzXs]="24" nzFor="relatedPassword">{{ 'mxk.accounts.relatedPassword' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid relatedPassword!">
<nz-input-group [nzSuffix]="suffixPasswordTemplate" style="width: 100%">
<input
[type]="passwordVisible ? 'text' : 'password'"
nz-input
placeholder="new password"
[(ngModel)]="form.model.relatedPassword"
[ngModelOptions]="{ standalone: true }"
name="relatedPassword"
id="relatedPassword"
/>
</nz-input-group>
<ng-template #suffixPasswordTemplate>
<i nz-icon [nzType]="passwordVisible ? 'eye-invisible' : 'eye'" (click)="passwordVisible = !passwordVisible"></i>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item style="width: 100%">
<nz-form-label nzRequired [nzSm]="6" [nzXs]="24" nzFor="confirmPassword">{{ 'mxk.password.confirmPassword' | i18n }}</nz-form-label>
<nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid confirmPassword!">
<nz-input-group [nzSuffix]="suffixConfirmPasswordTemplate" style="width: 100%">
<input
[type]="confirmPasswordVisible ? 'text' : 'password'"
nz-input
placeholder="confirm password"
[(ngModel)]="form.model.confirmPassword"
[ngModelOptions]="{ standalone: true }"
name="confirmPassword"
id="confirmPassword"
/>
</nz-input-group>
<ng-template #suffixConfirmPasswordTemplate>
<i
nz-icon
[nzType]="confirmPasswordVisible ? 'eye-invisible' : 'eye'"
(click)="confirmPasswordVisible = !confirmPasswordVisible"
></i>
</ng-template>
</nz-form-control>
</nz-form-item>
</form>
<div *nzModalFooter>
<button nz-button nzType="default" (click)="onClose($event)">{{ 'mxk.text.close' | i18n }}</button>
<button nz-button nzType="primary" (click)="onSubmit()">{{ 'mxk.text.submit' | i18n }}</button>
</div>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AccoutsComponent } from './accouts.component';
describe('AccoutsComponent', () => {
let component: AccoutsComponent;
let fixture: ComponentFixture<AccoutsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AccoutsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AccoutsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,79 @@
import { Component, ChangeDetectorRef, OnInit, Input } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { environment } from '@env/environment';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
import { Accounts } from '../../../entity/Accounts';
import { AccountsService } from '../../../service/accounts.service';
@Component({
selector: 'app-accouts',
templateUrl: './accouts.component.html',
styleUrls: ['./accouts.component.less']
})
export class AccoutsComponent implements OnInit {
@Input() appId?: String;
form: {
submitting: boolean;
model: Accounts;
} = {
submitting: false,
model: new Accounts()
};
redirect_uri: string = '';
formGroup: FormGroup = new FormGroup({});
confirmPasswordVisible = false;
passwordVisible = false;
constructor(
fb: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private modalRef: NzModalRef,
private accountsService: AccountsService,
private msg: NzMessageService,
private cdr: ChangeDetectorRef
) { }
ngOnInit(): void {
if (this.appId) {
this.accountsService.get(this.appId).subscribe(res => {
console.log(res.data);
this.form.model.init(res.data);
this.cdr.detectChanges();
});
}
}
onClose(e: MouseEvent): void {
e.preventDefault();
this.modalRef.destroy({ refresh: false });
}
onSubmit(): void {
this.form.submitting = true;
this.form.model.trans();
//if (this.formGroup.valid) {
this.accountsService.update(this.form.model).subscribe(res => {
if (res.code == 0) {
this.form.model.init(res.data);
this.msg.success(`提交成功`);
if (this.redirect_uri) {
window.location.href = `${environment.api.baseUrl}${this.redirect_uri}`;
}
} else {
this.msg.success(`提交失败`);
}
});
// } else {
// this.formGroup.updateValueAndValidity({ onlySelf: true });
// this.msg.success(`提交失败`);
//}
this.form.submitting = false;
this.cdr.detectChanges();
}
}

View File

@ -33,6 +33,7 @@ import { ProfileComponent } from './profile/profile.component';
import { SocialsAssociateComponent } from './socials-associate/socials-associate.component';
import { SocialsProviderComponent } from './socials-provider/socials-provider.component';
import { TimebasedComponent } from './timebased/timebased.component';
import { AccoutsComponent } from './accouts/accouts.component';
const routes: Routes = [
{
@ -62,7 +63,8 @@ const COMPONENTS = [ProfileComponent];
TimebasedComponent,
SocialsAssociateComponent,
PasswordComponent,
ProfileComponent
ProfileComponent,
AccoutsComponent
],
imports: [SharedModule, CommonModule, RouterModule.forChild(routes)],
exports: [RouterModule]

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { CountDownModule } from '@delon/abc/count-down';
import { OnboardingModule } from '@delon/abc/onboarding';

View File

@ -34,7 +34,7 @@
</ng-template>-->
<nz-card-meta [nzTitle]="item.title" [nzAvatar]="nzAvatar" *ngIf="item.protocol == 'Form_Based'">
<ng-template #nzAvatar>
<i nz-icon nzType="setting" nzTheme="outline"></i>
<i nz-icon nzType="user-add" nzTheme="outline" (click)="setAccount(item.id)"> </i>
<!--<nz-avatar nzSize="small" [nzSrc]="item.iconBase64"></nz-avatar>-->
</ng-template>
</nz-card-meta>

View File

@ -14,17 +14,23 @@
* limitations under the License.
*/
import { Platform } from '@angular/cdk/platform';
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, Renderer2 } from '@angular/core';
import { ChangeDetectionStrategy, ViewContainerRef, ChangeDetectorRef, Component, Inject, OnInit, Renderer2 } from '@angular/core';
import type { Chart } from '@antv/g2';
import { OnboardingService } from '@delon/abc/onboarding';
import { ACLService } from '@delon/acl';
import { environment } from '@env/environment';
import { format } from 'date-fns';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { NzModalRef, NzModalService } from 'ng-zorro-antd/modal';
import { CONSTS } from 'src/app/shared/consts';
import { AppListService } from '../../../service/appList.service';
import { AuthnService } from '../../../service/authn.service';
import { AccoutsComponent } from '../../config/accouts/accouts.component';
import { Console } from 'console';
@Component({
selector: 'app-home',
@ -47,6 +53,8 @@ export class HomeComponent implements OnInit {
baseUrl: String = '';
constructor(
private modal: NzModalService,
private viewContainerRef: ViewContainerRef,
private appListService: AppListService,
private cdr: ChangeDetectorRef,
private obSrv: OnboardingService,
@ -77,15 +85,26 @@ export class HomeComponent implements OnInit {
}
window.open(`${this.baseUrl}/authz/${appId}`);
}
setAccount(appId: string): void {
const modal = this.modal.create({
nzContent: AccoutsComponent,
nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {
appId: appId
},
nzWidth: 550
//nzOnOk: () => new Promise(resolve => setTimeout(resolve, 1000))
});
}
ngOnInit(): void {
if (environment.api.baseUrl.endsWith('/')) {
this.baseUrl = environment.api.baseUrl.substring(0, environment.api.baseUrl.length - 1);
} else {
this.baseUrl = environment.api.baseUrl;
}
this.appListService.appList().subscribe(res => {
console.log(res.data);
//console.log(res.data);
this.appList = res.data;
this.cdr.detectChanges();
});

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Component } from '@angular/core';
import { ACLService } from '@delon/acl';
import { MenuService } from '@delon/theme';

View File

@ -14,13 +14,12 @@
* limitations under the License.
*/
import { Inject, Optional, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ReuseTabService } from '@delon/abc/reuse-tab';
import { SettingsService } from '@delon/theme';
import { AuthenticationService } from '../../service/authentication.service';
import { AuthnService } from '../../service/authn.service';
import { SocialsProviderService } from '../../service/socials-provider.service';
@Component({
@ -34,7 +33,7 @@ export class CallbackComponent implements OnInit {
private router: Router,
private socialsProviderService: SocialsProviderService,
private settingsService: SettingsService,
private authenticationService: AuthenticationService,
private authnService: AuthnService,
@Optional()
@Inject(ReuseTabService)
private reuseTabService: ReuseTabService,
@ -49,9 +48,9 @@ export class CallbackComponent implements OnInit {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authenticationService.auth(res.data);
this.authnService.auth(res.data);
}
this.authenticationService.navigate({});
this.authnService.navigate({});
});
} else {
this.socialsProviderService.bind(this.provider, this.route.snapshot.queryParams).subscribe(res => {

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, OnDestroy, AfterViewInit, Optional } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
@ -27,7 +26,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
import { NzTabChangeEvent } from 'ng-zorro-antd/tabs';
import { finalize } from 'rxjs/operators';
import { AuthenticationService } from '../../../service/authentication.service';
import { AuthnService } from '../../../service/authn.service';
import { ImageCaptchaService } from '../../../service/image-captcha.service';
import { SocialsProviderService } from '../../../service/socials-provider.service';
import { CONSTS } from '../../../shared/consts';
@ -65,7 +64,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
fb: FormBuilder,
private router: Router,
private settingsService: SettingsService,
private authenticationService: AuthenticationService,
private authnService: AuthnService,
private socialsProviderService: SocialsProviderService,
private imageCaptchaService: ImageCaptchaService,
@Optional()
@ -88,7 +87,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
ngOnInit(): void {
//set redirect_uri , is BASE64URL
if (this.route.snapshot.queryParams[CONSTS.REDIRECT_URI]) {
this.authenticationService.setRedirectUri(this.route.snapshot.queryParams[CONSTS.REDIRECT_URI]);
this.authnService.setRedirectUri(this.route.snapshot.queryParams[CONSTS.REDIRECT_URI]);
}
//congress login
@ -97,8 +96,8 @@ export class UserLoginComponent implements OnInit, OnDestroy {
}
//init socials,state
this.authenticationService.clear();
this.authenticationService
this.authnService.clear();
this.authnService
.get({ remember_me: localStorage.getItem(CONSTS.REMEMBER) })
.pipe(
finalize(() => {
@ -118,8 +117,8 @@ export class UserLoginComponent implements OnInit, OnDestroy {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authenticationService.auth(res.data);
this.authenticationService.navigate({});
this.authnService.auth(res.data);
this.authnService.navigate({});
} else {
this.socials = res.data.socials;
this.state = res.data.state;
@ -136,7 +135,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
}
congressLogin(congress: string) {
this.authenticationService
this.authnService
.congress({
congress: congress
})
@ -154,8 +153,8 @@ export class UserLoginComponent implements OnInit, OnDestroy {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authenticationService.auth(res.data);
this.authenticationService.navigate({});
this.authnService.auth(res.data);
this.authnService.navigate({});
}
});
}
@ -199,7 +198,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
this.mobile.updateValueAndValidity({ onlySelf: true });
return;
}
this.authenticationService.produceOtp({ mobile: this.mobile.value }).subscribe(res => {
this.authnService.produceOtp({ mobile: this.mobile.value }).subscribe(res => {
if (res.code !== 0) {
this.msg.success(`发送失败`);
}
@ -242,7 +241,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
this.loading = true;
this.cdr.detectChanges();
this.authenticationService
this.authnService
.login({
authType: this.loginType,
state: this.state,
@ -271,8 +270,8 @@ export class UserLoginComponent implements OnInit, OnDestroy {
// 清空路由复用信息
this.reuseTabService.clear();
// 设置用户Token信息
this.authenticationService.auth(res.data);
this.authenticationService.navigate({});
this.authnService.auth(res.data);
this.authnService.navigate({});
}
this.cdr.detectChanges();
});
@ -287,7 +286,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
}
getQrCode(): void {
this.authenticationService.clearUser();
this.authnService.clearUser();
this.socialsProviderService.scanqrcode(this.socials.qrScan).subscribe(res => {
if (res.code === 0) {
if (this.socials.qrScan === 'workweixin') {

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SimpleGuard } from '@delon/auth';

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { NgModule, Type } from '@angular/core';
import { SharedModule } from '@shared';

View File

@ -14,10 +14,10 @@
* limitations under the License.
*/
import { Injectable, Inject } from '@angular/core';
import { Router } from '@angular/router';
import { StartupService } from '@core';
import { ACLService } from '@delon/acl';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
import { SettingsService, _HttpClient, User } from '@delon/theme';
import * as CryptoJS from 'crypto-js';
@ -29,7 +29,7 @@ import { hostname } from 'os';
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
export class AuthnService {
redirect_uri: string = '';
constructor(
@ -91,6 +91,7 @@ export class AuthenticationService {
this.cookieService.set(CONSTS.CONGRESS, authJwt.token);
this.cookieService.set(CONSTS.ONLINE_TICKET, authJwt.ticket, { domain: subHostName });
if (authJwt.remeberMe) {
localStorage.setItem(CONSTS.REMEMBER, authJwt.remeberMe);
}
@ -99,6 +100,26 @@ export class AuthenticationService {
this.tokenService.get()?.expired;
}
setRoles(aclService: ACLService | null): string[] {
let authorities: string[] = JSON.parse(localStorage.getItem(CONSTS.TOKEN) || '')?.authorities || [];
if (aclService) {
aclService.setRole(authorities);
}
return authorities;
}
hasRole(role: string): boolean {
if (role) {
let authorities: string[] = JSON.parse(localStorage.getItem(CONSTS.TOKEN) || '')?.authorities || [];
for (let i = 0; i < authorities.length; i++) {
if (role == authorities[i]) {
return true;
}
}
}
return false;
}
navigate(authJwt: any) {
// 重新获取 StartupService 内容,我们始终认为应用信息一般都会受当前用户授权范围而影响
this.startupService.load().subscribe(() => {

View File

@ -14,11 +14,11 @@
* limitations under the License.
*/
export const CONSTS = {
CONGRESS: 'congress',
ONLINE_TICKET: 'online_ticket',
REDIRECT_URI: 'redirect_uri',
REMEMBER: 'remember_me',
TOKEN: '_token',
VERSION: 'v3.5.0 GA'
};

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { PageHeaderModule } from '@delon/abc/page-header';
import { ResultModule } from '@delon/abc/result';
import { SEModule } from '@delon/abc/se';

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { NzAlertModule } from 'ng-zorro-antd/alert';
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
import { NzBadgeModule } from 'ng-zorro-antd/badge';

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule, Type } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@ -49,6 +48,7 @@ const DIRECTIVES: Array<Type<any>> = [];
RouterModule,
ReactiveFormsModule,
AlainThemeModule.forChild(),
DelonACLModule.forRoot(),
DelonACLModule,
DelonFormModule,
...SHARED_DELON_MODULES,

View File

@ -2,21 +2,23 @@
<ul nz-menu nzMode="horizontal" nzTheme="dark" style="text-align: center">
<!--一级菜单-->
<ng-container *ngFor="let mnav of ls">
<li nz-menu-item *ngIf="mnav.children.length == 0" style="min-width: 80px">
<a href="#{{ mnav.link }}">{{ mnav.text }}</a>
<li nz-menu-item *ngIf="mnav.children.length == 0 && mnav.disabled == false" style="min-width: 80px">
<a *ngIf="mnav.externalLink == ''" href="#{{ mnav.link }}">{{ mnav.text }}</a>
<a *ngIf="mnav.externalLink != ''" href="#{{ mnav.externalLink }}" target="_blank">{{ mnav.text }}</a>
</li>
<li nz-submenu nzTitle="{{ mnav.text }}" *ngIf="mnav.children.length > 0">
<ul>
<!--二级菜单-->
<ng-container *ngFor="let snav of mnav.children">
<li nz-menu-item *ngIf="snav.children.length == 0">
<li nz-menu-item *ngIf="snav.children.length == 0 && mnav.disabled == false">
<a href="#{{ snav.link }}">{{ snav.text }}</a>
</li>
<li nz-submenu nzTitle="{{ snav.text }}" *ngIf="snav.children.length > 0">
<ul>
<!--三级菜单-->
<ng-container *ngFor="let tnav of snav.children">
<li nz-menu-item *ngIf="tnav.children.length == 0">
<li nz-menu-item *ngIf="tnav.children.length == 0 && mnav.disabled == false">
<a href="#{{ tnav.link }}">{{ tnav.text }}</a>
</li>
</ng-container>

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Direction, Directionality } from '@angular/cdk/bidi';
import { DOCUMENT } from '@angular/common';
import {
@ -34,6 +33,7 @@ import {
} from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { NavigationEnd, Router } from '@angular/router';
import { ACLService } from '@delon/acl';
import { Menu, MenuIcon, MenuInner, MenuService, SettingsService } from '@delon/theme';
import { BooleanInput, InputBoolean, InputNumber, NumberInput, ZoneOutside } from '@delon/util/decorator';
import { WINDOW } from '@delon/util/token';
@ -41,6 +41,7 @@ import type { NzSafeAny } from 'ng-zorro-antd/core/types';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { AuthnService } from '../../service/authn.service';
import { LayoutDefaultOptions } from './types';
export interface Nav extends MenuInner {
@ -89,6 +90,8 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy {
constructor(
private menuSrv: MenuService,
private authnService: AuthnService,
private aclService: ACLService,
private settings: SettingsService,
private router: Router,
private render: Renderer2,
@ -280,6 +283,8 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.aclService.setFull(false);
this.authnService.setRoles(this.aclService);
const { doc, router, destroy$, menuSrv, settings, cdr } = this;
this.bodyEl = doc.querySelector('body');
this.openedByUrl(router.url);
@ -295,6 +300,11 @@ export class LayoutDefaultNavComponent implements OnInit, OnDestroy {
i._hidden = true;
}
}
if (i.acl && !this.authnService.hasRole(`${i.acl}`)) {
i.disabled = true;
}
if (this.openStrictly) {
i._open = i.open != null ? i.open : false;
}

View File

@ -14,11 +14,10 @@
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { DelonACLModule } from '@delon/acl';
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
@ -44,6 +43,7 @@ const COMPONENTS = [
imports: [
CommonModule,
RouterModule,
DelonACLModule.forRoot(),
NzToolTipModule,
NzIconModule,
NzAvatarModule,

View File

@ -16,6 +16,7 @@
"icon": "anticon-home",
"link": "/dashboard/home",
"open":false,
"acl": "ROLE_USER",
"children": []
},
{
@ -24,6 +25,7 @@
"link": "/access/sessions",
"icon": "anticon-cluster",
"open":false,
"acl": "ROLE_USER",
"children": []
},
{
@ -32,12 +34,14 @@
"link": "/config/setting",
"icon": "anticon-setting",
"open":false,
"acl": "ROLE_USER",
"children": [
{
"text": "我的资料",
"i18n": "mxk.menu.config.profile",
"link": "/config/profile",
"icon": "anticon-appstore",
"acl": "ROLE_USER",
"children": []
},
{
@ -45,6 +49,7 @@
"i18n": "mxk.menu.config.password",
"link": "/config/password",
"icon": "anticon-control",
"acl": "ROLE_USER",
"children": []
},
{
@ -52,6 +57,7 @@
"i18n": "mxk.menu.config.socialsassociate",
"link": "/config/socialsassociate",
"icon": "anticon-comment",
"acl": "ROLE_USER",
"children": []
},
{
@ -59,6 +65,7 @@
"i18n": "mxk.menu.config.timebased",
"link": "/config/timebased",
"icon": "anticon-send",
"acl": "ROLE_USER",
"children": []
}
]
@ -69,12 +76,14 @@
"link": "/audit/audit-logins",
"icon": "anticon-history",
"open":true,
"acl": "ROLE_USER",
"children": [
{
"text": "登录日志",
"i18n": "mxk.menu.audit.logins",
"link": "/audit/audit-logins",
"icon": "anticon-audit",
"acl": "ROLE_USER",
"children": []
},
{
@ -82,6 +91,7 @@
"i18n": "mxk.menu.audit.loginapps",
"link": "/audit/audit-login-apps",
"icon": "anticon-audit",
"acl": "ROLE_USER",
"children": []
}
]
@ -90,8 +100,10 @@
{
"text": "后台",
"i18n": "mxk.menu.mgt",
"link": "/mgt",
"link": "/authz/mgt",
"externalLink": "/authz/mgt",
"icon": "anticon-cluster",
"acl": "ROLE_ADMINISTRATORS",
"open":false,
"children": []
}

View File

@ -204,7 +204,7 @@
},
"accounts":{
"userId":"用户编号",
"username":"登录账号",
"username":"登录",
"displayName":"用户名",
"appName":"应用名称",
"appId":"应用编号",

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
/*
* Automatically generated by 'ng g ng-alain:plugin icon'
* @see https://ng-alain.com/cli/plugin#icon
@ -107,6 +106,7 @@ import {
DashOutline,
FileProtectOutline,
HistoryOutline,
UserAddOutline,
AuditOutline
} from '@ant-design/icons-angular/icons';
import { QR_DEFULAT_CONFIG } from '@delon/abc/qr';
@ -198,5 +198,6 @@ export const ICONS_AUTO = [
DashOutline,
FileProtectOutline,
HistoryOutline,
UserAddOutline,
AuditOutline
];

View File

@ -1,5 +1,5 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* 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.
@ -20,7 +20,6 @@ package org.maxkey.web.contorller;
import java.util.List;
import org.maxkey.authn.annotation.CurrentUser;
import org.maxkey.authn.web.AuthorizationUtils;
import org.maxkey.constants.ConstsStatus;
import org.maxkey.crypto.password.PasswordReciprocal;
import org.maxkey.entity.Accounts;
@ -115,7 +114,6 @@ public class AppListController {
@ModelAttribute Accounts account,
@CurrentUser UserInfo currentUser) {
Accounts appUsers = new Accounts();
if (credential == Apps.CREDENTIALS.USER_DEFINED) {
appUsers = accountsService.load(new Accounts(currentUser.getId(), account.getAppId()));
if (appUsers == null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
* 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.
@ -18,12 +18,13 @@
package org.maxkey.web.contorller;
import java.awt.image.BufferedImage;
import java.util.Base64;
import java.util.HashMap;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.maxkey.authn.annotation.CurrentUser;
import org.maxkey.crypto.Base32Utils;
import org.maxkey.crypto.Base64Utils;
import org.maxkey.crypto.password.PasswordReciprocal;
import org.maxkey.entity.Message;
import org.maxkey.entity.UserInfo;
@ -31,7 +32,6 @@ import org.maxkey.password.onetimepwd.algorithm.OtpKeyUriFormat;
import org.maxkey.password.onetimepwd.algorithm.OtpSecret;
import org.maxkey.persistence.service.UserInfoService;
import org.maxkey.util.RQCodeUtils;
import org.maxkey.web.image.ImageEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -60,7 +60,8 @@ public class OneTimePasswordController {
@RequestMapping(value = {"/timebased"})
@ResponseBody
public ResponseEntity<?> timebased(@RequestParam String generate,@CurrentUser UserInfo currentUser) {
public ResponseEntity<?> timebased(
@RequestParam String generate,@CurrentUser UserInfo currentUser) {
HashMap<String,Object >timebased =new HashMap<String,Object >();
generate(generate,currentUser);
@ -72,6 +73,8 @@ public class OneTimePasswordController {
String otpauth = otpKeyUriFormat.format(currentUser.getUsername());
byte[] byteSharedSecret = Base32Utils.decode(sharedSecret);
String hexSharedSecret = Hex.encodeHexString(byteSharedSecret);
BufferedImage bufferedImage = RQCodeUtils.write2BufferedImage(otpauth, "gif", 300, 300);
String rqCode = Base64Utils.encodeImage(bufferedImage);
timebased.put("displayName", currentUser.getDisplayName());
timebased.put("username", currentUser.getUsername());
@ -79,7 +82,7 @@ public class OneTimePasswordController {
timebased.put("period", otpKeyUriFormat.getPeriod());
timebased.put("sharedSecret", sharedSecret);
timebased.put("hexSharedSecret", hexSharedSecret);
timebased.put("rqCode", genRqCode(otpauth));
timebased.put("rqCode", rqCode);
return new Message<HashMap<String,Object >>(timebased).buildResponse();
}
@ -97,9 +100,4 @@ public class OneTimePasswordController {
}
}
public String genRqCode(String otpauth) {
BufferedImage bufferedImage = RQCodeUtils.write2BufferedImage(otpauth, "gif", 300, 300);
byte[] imageByte = ImageEndpoint.bufferedImage2Byte(bufferedImage);
return "data:image/png;base64," + Base64.getEncoder().encodeToString(imageByte);
}
}