mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-10 11:40:01 +08:00
[ISSUE-205|247] Support SSL + Login
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.config;
|
||||
|
||||
import org.apache.rocketmq.console.interceptor.AuthInterceptor;
|
||||
import org.apache.rocketmq.console.model.UserInfo;
|
||||
import org.apache.rocketmq.console.util.WebUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
||||
@Autowired
|
||||
@Qualifier("authInterceptor")
|
||||
private AuthInterceptor authInterceptor;
|
||||
|
||||
@Resource
|
||||
RMQConfigure configure;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
if (configure.isLoginRequired()) {
|
||||
registry.addInterceptor(authInterceptor).excludePathPatterns("/error", "/user/guide/**", "/login/**");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new HandlerMethodArgumentResolver() {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter methodParameter) {
|
||||
return methodParameter.getParameterType().isAssignableFrom(UserInfo.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
|
||||
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
|
||||
UserInfo userInfo = (UserInfo) WebUtil.getAttribute((ServletRequest) nativeWebRequest.getNativeRequest(),
|
||||
UserInfo.USER_INFO);
|
||||
if (userInfo != null) {
|
||||
return userInfo;
|
||||
}
|
||||
throw new MissingServletRequestPartException(UserInfo.USER_INFO);
|
||||
}
|
||||
});
|
||||
|
||||
super.addArgumentResolvers(argumentResolvers); //REVIEW ME
|
||||
}
|
||||
}
|
@@ -17,11 +17,15 @@
|
||||
package org.apache.rocketmq.console.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.console.util.CipherHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
|
||||
@@ -37,12 +41,25 @@ public class RMQConfigure {
|
||||
private volatile String isVIPChannel = System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true");
|
||||
|
||||
|
||||
private String dataPath;
|
||||
private String dataPath = "/tmp/rocketmq-console/data";
|
||||
|
||||
private boolean enableDashBoardCollect;
|
||||
|
||||
private String msgTrackTopicName;
|
||||
|
||||
private String ciperKey = "rocketmqrocketmq";
|
||||
|
||||
private boolean loginRequired = false;
|
||||
|
||||
public String getCiperKey() {
|
||||
return ciperKey;
|
||||
}
|
||||
|
||||
public void setCiperKey(String ciperKey) {
|
||||
this.ciperKey = ciperKey;
|
||||
}
|
||||
|
||||
|
||||
public String getNamesrvAddr() {
|
||||
return namesrvAddr;
|
||||
}
|
||||
@@ -94,4 +111,18 @@ public class RMQConfigure {
|
||||
public void setMsgTrackTopicName(String msgTrackTopicName) {
|
||||
this.msgTrackTopicName = msgTrackTopicName;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CipherHelper cipherHelper() throws UnsupportedEncodingException {
|
||||
CipherHelper cipherHelper = new CipherHelper(getCiperKey());
|
||||
return cipherHelper;
|
||||
}
|
||||
|
||||
public boolean isLoginRequired() {
|
||||
return loginRequired;
|
||||
}
|
||||
|
||||
public void setLoginRequired(boolean loginRequired) {
|
||||
this.loginRequired = loginRequired;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.controller;
|
||||
|
||||
import org.apache.rocketmq.console.model.User;
|
||||
import org.apache.rocketmq.console.service.UserService;
|
||||
import org.apache.rocketmq.console.util.CipherHelper;
|
||||
import org.apache.rocketmq.console.util.WebUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
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.ResponseBody;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/login")
|
||||
public class LoginController {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private CipherHelper cipherHelper;
|
||||
|
||||
@RequestMapping(value = "/index", method = RequestMethod.GET)
|
||||
public String index(Map<String, Object> map) {
|
||||
return "login";
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/check", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object check(@RequestParam("username") String username,
|
||||
@RequestParam(value = "password") String password,
|
||||
HttpServletResponse response) throws Exception {
|
||||
logger.info("user:{} login", username);
|
||||
User user = userService.queryByUsernameAndPassword(username, password);
|
||||
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("Bad username or password!");
|
||||
} else {
|
||||
WebUtil.setCookie(response, WebUtil.LOGIN_TOKEN, cipherHelper.encrypt(username), false);
|
||||
WebUtil.setCookie(response, WebUtil.USER_NAME, username, false);
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/logout", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public void logout(HttpServletResponse response) {
|
||||
WebUtil.setCookie(response, WebUtil.LOGIN_TOKEN,"", true);
|
||||
WebUtil.setCookie(response, WebUtil.USER_NAME, "", true);
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.interceptor;
|
||||
|
||||
import org.apache.rocketmq.console.model.UserInfo;
|
||||
import org.apache.rocketmq.console.service.LoginService;
|
||||
import org.apache.rocketmq.console.util.WebUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@Component
|
||||
public class AuthInterceptor extends HandlerInterceptorAdapter {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Autowired
|
||||
private LoginService loginService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
boolean ok = loginService.login(request, response);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UserInfo userInfo = loginService.parse(request, response);
|
||||
if (userInfo.getUser() == null) {
|
||||
logger.warn("cannot parse userinfo, userInfo:{}", userInfo);
|
||||
WebUtil.print(response, "User does not exist or wrong password");
|
||||
return false;
|
||||
}
|
||||
WebUtil.setAttribute(request, UserInfo.USER_INFO, userInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
79
src/main/java/org/apache/rocketmq/console/model/User.java
Normal file
79
src/main/java/org/apache/rocketmq/console/model/User.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.model;
|
||||
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
public class User {
|
||||
public static final int ORDINARY = 0;
|
||||
public static final int ADMIN = 1;
|
||||
|
||||
private long id;
|
||||
private String name;
|
||||
private String password;
|
||||
@Range(min = 0, max = 1)
|
||||
private int type = 0;
|
||||
|
||||
|
||||
public User(String name, String password, int type) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", type=" + type +
|
||||
'}';
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.model;
|
||||
|
||||
public class UserInfo {
|
||||
public static final String USER_INFO = "userInfo";
|
||||
private User user;
|
||||
private long loginTime;
|
||||
private String ip;
|
||||
|
||||
public long getLoginTime() {
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public void setLoginTime(long loginTime) {
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UserInfo{" +
|
||||
"user=" + user +
|
||||
", loginTime=" + loginTime +
|
||||
", ip='" + ip + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.console.model.UserInfo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface LoginService {
|
||||
String getLoginId(HttpServletRequest request);
|
||||
boolean login(HttpServletRequest request, HttpServletResponse response);
|
||||
UserInfo parse(HttpServletRequest request, HttpServletResponse response);
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.console.model.User;
|
||||
|
||||
public interface UserService {
|
||||
User queryByName(String name);
|
||||
|
||||
User queryByUsernameAndPassword(String username, String password);
|
||||
}
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.service.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.model.UserInfo;
|
||||
import org.apache.rocketmq.console.service.LoginService;
|
||||
import org.apache.rocketmq.console.service.UserService;
|
||||
import org.apache.rocketmq.console.util.CipherHelper;
|
||||
import org.apache.rocketmq.console.util.WebUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
@Service
|
||||
public class LoginServiceImpl implements LoginService {
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Resource
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
@Resource
|
||||
private CipherHelper cipherHelper;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public String getLoginId(HttpServletRequest request) {
|
||||
String loginToken = WebUtil.getLoginCookieValue(request);
|
||||
if (loginToken != null) {
|
||||
String userName = cipherHelper.decrypt(loginToken);
|
||||
if (StringUtils.isNotBlank(loginToken)) {
|
||||
WebUtil.setAttribute(request, "username", userName);
|
||||
return userName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getLoginId(String ticket) {
|
||||
// You can extend this func to support external ticket
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login(HttpServletRequest request, HttpServletResponse response) {
|
||||
String username = getLoginId(request);
|
||||
if (username != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auth(request, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserInfo parse(HttpServletRequest request, HttpServletResponse response) {
|
||||
String ip = WebUtil.getIp(request);
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setIp(ip);
|
||||
userInfo.setLoginTime(System.currentTimeMillis());
|
||||
|
||||
Object username = WebUtil.getAttribute(request, "username");
|
||||
if (username == null) {
|
||||
userInfo.setUser(null);
|
||||
} else {
|
||||
userInfo.setUser(userService.queryByName(username.toString()));
|
||||
}
|
||||
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
private String parseRedirect(HttpServletRequest request) throws Exception {
|
||||
try {
|
||||
String redirect = request.getParameter("redirect");
|
||||
String url = null;
|
||||
if (redirect != null) {
|
||||
url = URLDecoder.decode(redirect, "UTF-8");
|
||||
}
|
||||
if (url != null) {
|
||||
logger.info("redirect to:" + url);
|
||||
return url;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("", e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
protected void auth(HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
String url = WebUtil.getUrl(request);
|
||||
try {
|
||||
url = URLEncoder.encode(url, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error("url encode:{}", url, e);
|
||||
}
|
||||
WebUtil.redirect(response, request, "/#/login?redirect=" + url);
|
||||
} catch (IOException e) {
|
||||
logger.error("redirect err", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.service.impl;
|
||||
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.model.User;
|
||||
import org.apache.rocketmq.console.service.UserService;
|
||||
import org.apache.rocketmq.srvutil.FileWatchService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService, InitializingBean {
|
||||
@Resource
|
||||
RMQConfigure configure;
|
||||
|
||||
FileBasedUserInfoStore fileBasedUserInfoStore;
|
||||
|
||||
@Override
|
||||
public User queryByName(String name) {
|
||||
return fileBasedUserInfoStore.queryByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User queryByUsernameAndPassword(String username, String password) {
|
||||
return fileBasedUserInfoStore.queryByUsernameAndPassword(username, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (configure.isEnableDashBoardCollect()) {
|
||||
fileBasedUserInfoStore = new FileBasedUserInfoStore(configure);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileBasedUserInfoStore {
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
private static final String FILE_NAME = "users.properties";
|
||||
|
||||
private String filePath;
|
||||
private final Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public FileBasedUserInfoStore(RMQConfigure configure) {
|
||||
log.info("XXXXXX " + configure);
|
||||
filePath = configure.getRocketMqConsoleDataPath() + File.separator + FILE_NAME;
|
||||
load();
|
||||
}
|
||||
|
||||
private void load() {
|
||||
|
||||
Properties prop = new Properties();
|
||||
try {
|
||||
prop.load(new FileReader(filePath));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Failed to load loginUserInfo property file: %s", filePath));
|
||||
}
|
||||
|
||||
Map<String, User> loadUserMap = new HashMap<>();
|
||||
String[] arrs;
|
||||
int role;
|
||||
for (String key : prop.stringPropertyNames()) {
|
||||
String v = prop.getProperty(key);
|
||||
if (v == null) continue;
|
||||
arrs = v.split(",", 2);
|
||||
if (arrs.length == 0) {
|
||||
continue;
|
||||
} else if (arrs.length == 1) {
|
||||
role = 0;
|
||||
} else {
|
||||
role = Integer.parseInt(arrs[1].trim());
|
||||
}
|
||||
|
||||
loadUserMap.put(key, new User(key, arrs[0].trim(), role));
|
||||
}
|
||||
|
||||
|
||||
userMap.clear();
|
||||
userMap.putAll(loadUserMap);
|
||||
}
|
||||
|
||||
private boolean watch() {
|
||||
try {
|
||||
FileWatchService fileWatchService = new FileWatchService(new String[]{filePath}, new FileWatchService.Listener() {
|
||||
@Override
|
||||
public void onChanged(String path) {
|
||||
log.info("The loginUserInfo property file changed, reload the context");
|
||||
load();
|
||||
}
|
||||
});
|
||||
fileWatchService.start();
|
||||
log.info("Succeed to start LoginUserWatcherService");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to start LoginUserWatcherService", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public User queryByName(String name) {
|
||||
return userMap.get(name);
|
||||
}
|
||||
|
||||
public User queryByUsernameAndPassword(@NotNull String username, @NotNull String password) {
|
||||
User user = queryByName(username);
|
||||
if (user != null && password.equals(user.getPassword())) {
|
||||
return user;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.util;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class CipherHelper {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private static final String ALGORITHM = "AES";
|
||||
|
||||
private SecretKeySpec secretKeySpec;
|
||||
|
||||
public CipherHelper(String ciperKey) throws UnsupportedEncodingException {
|
||||
secretKeySpec = new SecretKeySpec(ciperKey.getBytes("UTF-8"), ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt
|
||||
* @param toBeEncrypt
|
||||
* @return
|
||||
*/
|
||||
public String encrypt(String toBeEncrypt) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
|
||||
byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
|
||||
return Base64.encodeBase64String(encrypted);
|
||||
} catch (Exception e) {
|
||||
logger.error("encrypt:{} err", toBeEncrypt, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt
|
||||
* @param encrypted
|
||||
* @return
|
||||
*/
|
||||
public String decrypt(String encrypted) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
|
||||
byte[] decryptedBytes = cipher.doFinal(Base64.decodeBase64(encrypted));
|
||||
return new String(decryptedBytes);
|
||||
} catch (Exception e) {
|
||||
logger.error("decrypt:{} err", encrypted, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
164
src/main/java/org/apache/rocketmq/console/util/WebUtil.java
Normal file
164
src/main/java/org/apache/rocketmq/console/util/WebUtil.java
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.rocketmq.console.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class WebUtil {
|
||||
public static final String LOGIN_TOKEN = "TOKEN";
|
||||
public static final String USER_NAME = "username";
|
||||
|
||||
/**
|
||||
* Obtain ServletRequest header value
|
||||
*
|
||||
* @param request
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static String getHeaderValue(HttpServletRequest request, String name) {
|
||||
String v = request.getHeader(name);
|
||||
if (v == null) {
|
||||
return null;
|
||||
}
|
||||
return v.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch request ip address
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static String getIp(ServletRequest request) {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
String addr = getHeaderValue(req, "X-Forwarded-For");
|
||||
if (StringUtils.isNotEmpty(addr) && addr.contains(",")) {
|
||||
addr = addr.split(",")[0];
|
||||
}
|
||||
if (StringUtils.isEmpty(addr)) {
|
||||
addr = getHeaderValue(req, "X-Real-IP");
|
||||
}
|
||||
if (StringUtils.isEmpty(addr)) {
|
||||
addr = req.getRemoteAddr();
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain cookie
|
||||
*
|
||||
* @param request
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static Cookie getCookie(HttpServletRequest request, String name) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (name.equals(cookie.getName())) {
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setCookie(HttpServletResponse response, String key, String value, boolean removeFlag) {
|
||||
Cookie cookie = new Cookie(key, value);
|
||||
cookie.setPath("/");
|
||||
if (removeFlag) {
|
||||
cookie.setMaxAge(0);
|
||||
}
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain login cookie info
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static String getLoginCookieValue(HttpServletRequest request) {
|
||||
Cookie cookie = getCookie(request, LOGIN_TOKEN);
|
||||
if (cookie != null) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void redirect(HttpServletResponse response, HttpServletRequest request, String path) throws IOException {
|
||||
response.sendRedirect(request.getContextPath() + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the full url path
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static String getUrl(HttpServletRequest request) {
|
||||
String url = request.getRequestURL().toString();
|
||||
String queryString = request.getQueryString();
|
||||
if (queryString != null) {
|
||||
url += "?" + request.getQueryString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch attribute form request
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static void setAttribute(ServletRequest request, String name, Object obj) {
|
||||
request.setAttribute(name, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attribute to request
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static Object getAttribute(ServletRequest request, String name) {
|
||||
return request.getAttribute(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write content to front-page/response
|
||||
*
|
||||
* @param response
|
||||
* @param result
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void print(HttpServletResponse response, String result) throws IOException {
|
||||
response.setContentType("text/html;charset=UTF-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.print(result);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
@@ -1,5 +1,12 @@
|
||||
server.contextPath=
|
||||
server.port=8080
|
||||
|
||||
### SSL setting
|
||||
#server.ssl.key-store=classpath:rmqcngkeystore.jks
|
||||
#server.ssl.key-store-password=rocketmq
|
||||
#server.ssl.keyStoreType=PKCS12
|
||||
#server.ssl.keyAlias=rmqcngkey
|
||||
|
||||
#spring.application.index=true
|
||||
spring.application.name=rocketmq-console
|
||||
spring.http.encoding.charset=UTF-8
|
||||
@@ -7,7 +14,7 @@ spring.http.encoding.enabled=true
|
||||
spring.http.encoding.force=true
|
||||
logging.config=classpath:logback.xml
|
||||
#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
|
||||
rocketmq.config.namesrvAddr=
|
||||
rocketmq.config.namesrvAddr=11.239.187.178:9876
|
||||
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
|
||||
rocketmq.config.isVIPChannel=
|
||||
#rocketmq-console's data path:dashboard/monitor
|
||||
@@ -15,4 +22,8 @@ rocketmq.config.dataPath=/tmp/rocketmq-console/data
|
||||
#set it false if you don't want use dashboard.default true
|
||||
rocketmq.config.enableDashBoardCollect=true
|
||||
#set the message track trace topic if you don't want use the default one
|
||||
rocketmq.config.msgTrackTopicName=
|
||||
rocketmq.config.msgTrackTopicName=
|
||||
rocketmq.config.ticketKey=ticket
|
||||
|
||||
#Must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
|
||||
rocketmq.config.loginRequired=true
|
BIN
src/main/resources/rmqcngkeystore.jks
Normal file
BIN
src/main/resources/rmqcngkeystore.jks
Normal file
Binary file not shown.
@@ -113,6 +113,6 @@
|
||||
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
|
||||
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
|
||||
<script type="text/javascript" src="vendor/preLoading/main.js"></script>
|
||||
|
||||
<script type="text/javascript" src="src/login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -29,8 +29,8 @@ var app = angular.module('app', [
|
||||
'localytics.directives',
|
||||
'pascalprecht.translate'
|
||||
]).run(
|
||||
['$rootScope','$location','$cookies',
|
||||
function ($rootScope,$location,$cookies) {
|
||||
['$rootScope','$location','$cookies','$http',
|
||||
function ($rootScope,$location,$cookies,$http) {
|
||||
// var filter = function(url){
|
||||
// var outFilterArrs = []
|
||||
// outFilterArrs.push("/login");
|
||||
@@ -51,6 +51,23 @@ var app = angular.module('app', [
|
||||
// chatApi.login();
|
||||
// }
|
||||
|
||||
$rootScope.username = $cookies.get("username");
|
||||
if (!angular.isDefined($rootScope.username)) {
|
||||
$rootScope.username = '';
|
||||
}
|
||||
console.log("username " + $rootScope.username);
|
||||
$rootScope.globals = $cookies.get('TOKEN');
|
||||
console.log('TOKEN ' + $rootScope.globals);
|
||||
$rootScope.$on('$locationChangeStart', function (event, next, current) {
|
||||
// redirect to login page if not logged in and trying to access a restricted page
|
||||
var restrictedPage = $.inArray($location.path(), ['/login']) === -1;
|
||||
var loggedIn = $rootScope.globals;
|
||||
if (restrictedPage && (!angular.isDefined(loggedIn) || !loggedIn)) {
|
||||
var callback = $location.path();
|
||||
$location.path('/login');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
var pathArray = $location.url().split("/");
|
||||
@@ -130,6 +147,9 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro
|
||||
$routeProvider.when('/', {
|
||||
templateUrl: 'view/pages/index.html',
|
||||
controller:'dashboardCtrl'
|
||||
}).when('/login', {
|
||||
templateUrl: 'view/pages/login.html',
|
||||
controller:'loginController'
|
||||
}).when('/cluster', {
|
||||
templateUrl: 'view/pages/cluster.html',
|
||||
controller:'clusterController'
|
||||
@@ -153,7 +173,7 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro
|
||||
controller:'opsController'
|
||||
}).when('/404', {
|
||||
templateUrl: '404'
|
||||
}).otherwise('404');
|
||||
}).otherwise('/login');
|
||||
|
||||
$translateProvider.translations('en',en);
|
||||
$translateProvider.translations('zh',zh);
|
||||
|
@@ -14,10 +14,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
app.controller('AppCtrl', ['$scope','$rootScope','$cookies','$location','$translate', function ($scope,$rootScope,$cookies,$location,$translate) {
|
||||
app.controller('AppCtrl', ['$scope','$rootScope','$cookies','$location','$translate','$http','Notification', function ($scope,$rootScope,$cookies,$location,$translate, $http, Notification) {
|
||||
$scope.changeTranslate = function(langKey){
|
||||
$translate.use(langKey);
|
||||
}
|
||||
|
||||
$scope.logout = function(){
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "login/logout"
|
||||
}).success(function (resp) {
|
||||
window.location = "/";
|
||||
$cookies.remove("username");
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
app.controller('dashboardCtrl', ['$scope','$rootScope','$translate','$filter','Notification','remoteApi','tools', function ($scope,$rootScope,$translate,$filter,Notification,remoteApi,tools) {
|
||||
|
@@ -77,5 +77,10 @@ var en = {
|
||||
"CLUSTER_NAME":"clusterName",
|
||||
"OPS":"OPS",
|
||||
"AUTO_REFRESH":"AUTO_REFRESH",
|
||||
"REFRESH":"REFRESH"
|
||||
"REFRESH":"REFRESH",
|
||||
"LOGOUT":"Logout",
|
||||
"LOGIN":"Login",
|
||||
"USER_NAME":"Username",
|
||||
"PASSWORD":"Password",
|
||||
"WELCOME":"Hi, welcome using RocketMQ Console"
|
||||
}
|
@@ -77,5 +77,10 @@ var zh = {
|
||||
"CLUSTER_NAME":"集群名",
|
||||
"OPS":"运维",
|
||||
"AUTO_REFRESH":"自动刷新",
|
||||
"REFRESH":"刷新"
|
||||
"REFRESH":"刷新",
|
||||
"LOGOUT":"退出",
|
||||
"LOGIN":"登录",
|
||||
"USER_NAME":"用户名",
|
||||
"PASSWORD":"密码",
|
||||
"WELCOME":"您好,欢迎使用RocketMQ控制台"
|
||||
}
|
42
src/main/resources/static/src/login.js
Normal file
42
src/main/resources/static/src/login.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.
|
||||
*/
|
||||
|
||||
app.controller('loginController', ['$scope','$location','$http','Notification','$cookies', function ($scope,$location,$http,Notification,$cookies) {
|
||||
$scope.login = function () {
|
||||
if(!$("#username").val()) {
|
||||
alert("用户名不能为空");
|
||||
return;
|
||||
}
|
||||
if(!$("#password").val()) {
|
||||
alert("密码不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "login/check",
|
||||
params:{username:$("#username").val(), password:$("#password").val()}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: 'Login successful, redirect now', delay: 2000});
|
||||
window.location = "/";
|
||||
} else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
}]);
|
@@ -28,7 +28,27 @@
|
||||
<li><a href="javascript:void(0)" ng-click="changeTranslate('zh')">Simplified Chinese</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown" ng-show="username != ''">
|
||||
<a href="bootstrap-elements.html" data-target="#" class="dropdown-toggle" data-toggle="dropdown">{{username}}
|
||||
<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="javascript:void(0)" ng-click="logout()">{{'LOGOUT' | translate}}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var app = angular.module('DemoApp',[]);
|
||||
|
||||
app.controller('DemoController',function($scope){
|
||||
$scope.IsVisible = false;
|
||||
|
||||
$scope.ShowHide = function(){
|
||||
$scope.IsVisible = $scope.IsVisible = true;
|
||||
}
|
||||
});
|
||||
</script>
|
44
src/main/resources/static/view/pages/login.html
Normal file
44
src/main/resources/static/view/pages/login.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<div id="loginModal" class="page-content" id="deployHistoryList" data-width="400" data-backdrop="static" role="main" >
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{'WELCOME' | translate}}</h4>
|
||||
</div>
|
||||
<form class="form-horizontal form-bordered form-row-stripped" id="loginForm">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="form-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-4">{{'USER_NAME' | translate}}: </label>
|
||||
<div class="col-md-5">
|
||||
<input type="text" id="username" name="username" placeholder="{{'USER_NAME' | translate}}" class="form-control" ng-model="filterStr"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-4"> {{'PASSWORD' | translate}}: </label>
|
||||
<div class="col-md-5">
|
||||
<input type="password" name="password" id="password"
|
||||
value="" placeholder="{{'USER_NAME' | translate}}"
|
||||
class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-click="login()">{{'LOGIN' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
console.log('aaaaa111');
|
||||
//$("#loginModal").modal("hide");
|
||||
console.log('aaaaa');
|
||||
})
|
||||
</script>
|
Reference in New Issue
Block a user