Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple login page auth for sentinel-dashboard #659

Merged
merged 8 commits into from
Apr 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion sentinel-dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ mvn clean package

```bash
java -Dserver.port=8080 \
-Dserver.servlet.session.timeout=7200 \
-Dauth.username=sentinel \
-Dauth.password=123456 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-Dproject.name=sentinel-dashboard \
-jar target/sentinel-dashboard.jar
```

上述命令中我们指定几个 JVM 参数,其中 `-Dserver.port=8080` 用于指定 Spring Boot 启动端口为 `8080`,其余几个是 Sentinel 客户端的参数。
上述命令中我们指定几个 JVM 参数,其中:
`-Dserver.port=8080` 用于指定 Spring Boot 启动端口为 `8080`;
`-Dserver.servlet.session.timeout=7200` 用于指定 Spring Boot 服务器端会话的过期时间,如不带后缀的7200表示7200秒,60m表示60分钟,默认为30分钟;
`-Dauth.username=sentinel`、 `-Dauth.password=123456` 用于指定控制台的登录用户和密码分别为sentinel和123456,如果省略这2个参数,默认用户和密码均为sentinel;
其余几个是 Sentinel 客户端的参数。
为便于演示,我们对控制台本身加入了流量控制功能,具体做法是引入 `CommonFilter` 这个 Sentinel 拦截器。上述 JVM 参数的含义是:

| 参数 | 作用 |
Expand Down
2 changes: 2 additions & 0 deletions sentinel-dashboard/Sentinel_Dashboard_Feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Sentinel 提供了多种规则来保护系统的不同部分。流量控制规

项 | 类型 | 默认值 | 最小值 | 描述
--- | --- | --- | --- | ---
sentinel.dashboard.auth.username | String | sentinel | 无 | 登录控制台的用户,默认sentinel
sentinel.dashboard.auth.password | String | sentinel | 无 | 登录控制台的密码,默认sentinel
sentinel.dashboard.app.hideAppNoMachineMillis | Integer | 0 | 60000 | 是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭
sentinel.dashboard.removeAppNoMachineMillis | Integer | 0 | 120000 | 是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭
sentinel.dashboard.unhealthyMachineMillis | Integer | 60000 | 30000 | 主机失联判定,不可关闭
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.dashboard.auth;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
* @author cdfive
* @since 1.6.0
*/
@Primary
@Component
public class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> {

public static final String WEB_SESSTION_KEY = "session_sentinel_admin";

@Override
public AuthUser getAuthUser(HttpServletRequest request) {
HttpSession session = request.getSession();
Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY);
if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) {
return (AuthUser) sentinelUserObj;
}

return null;
}

public static final class SimpleWebAuthUserImpl implements AuthUser {

private String username;

public SimpleWebAuthUserImpl(String username) {
this.username = username;
}

@Override
public boolean authTarget(String target, PrivilegeType privilegeType) {
return true;
}

@Override
public boolean isSuperUser() {
return true;
}

@Override
public String getNickName() {
return username;
}

@Override
public String getLoginName() {
return username;
}

@Override
public String getId() {
return username;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public class DashboardConfig {

public static final int DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS = 60_000;

/**
* Login username
*/
public static final String CONFIG_AUTH_USERNAME = "sentinel.dashboard.auth.username";

/**
* Login password
*/
public static final String CONFIG_AUTH_PASSWORD = "sentinel.dashboard.auth.password";

/**
* Hide application name in sidebar when it has no healthy machines after specific period in millisecond.
*/
Expand Down Expand Up @@ -70,7 +80,22 @@ private static String getConfig(String name) {
}
return "";
}


protected static String getConfigStr(String name) {
if (cacheMap.containsKey(name)) {
return (String) cacheMap.get(name);
}

String val = getConfig(name);

if (StringUtils.isBlank(val)) {
return null;
}

cacheMap.put(name, val);
return val;
}

protected static int getConfigInt(String name, int defaultVal, int minVal) {
if (cacheMap.containsKey(name)) {
return (int)cacheMap.get(name);
Expand All @@ -84,7 +109,15 @@ protected static int getConfigInt(String name, int defaultVal, int minVal) {
cacheMap.put(name, val);
return val;
}


public static String getAuthUsername() {
return getConfigStr(CONFIG_AUTH_USERNAME);
}

public static String getAuthPassword() {
return getConfigStr(CONFIG_AUTH_PASSWORD);
}

public static int getHideAppNoMachineMillis() {
return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,8 @@
*/
package com.alibaba.csp.sentinel.dashboard.config;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;

import com.alibaba.csp.sentinel.dashboard.filter.AuthFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -40,6 +27,8 @@
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;

/**
* @author leyou
*/
Expand All @@ -49,7 +38,7 @@ public class WebConfig implements WebMvcConfigurer {
private final Logger logger = LoggerFactory.getLogger(WebConfig.class);

@Autowired
private AuthService<HttpServletRequest> authService;
private AuthFilter authFilter;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
Expand Down Expand Up @@ -81,29 +70,7 @@ public FilterRegistrationBean sentinelFilterRegistration() {
@Bean
public FilterRegistrationBean authenticationFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new Filter() {

@Override
public void init(FilterConfig filterConfig) throws ServletException { }

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
AuthUser authUser = authService.getAuthUser(request);
// authentication fail
if (authUser == null) {
PrintWriter writer = servletResponse.getWriter();
writer.append("login needed");
writer.flush();
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}

@Override
public void destroy() { }
});
registration.setFilter(authFilter);
registration.addUrlPatterns("/*");
registration.setName("authenticationFilter");
registration.setOrder(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.dashboard.controller;

import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl;
import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
* @author cdfive
* @since 1.6.0
*/
@RestController
@RequestMapping(value = "/auth", produces = MediaType.APPLICATION_JSON_VALUE)
public class AuthController {

private static Logger LOGGER = LoggerFactory.getLogger(AuthController.class);

@Value("${auth.username:sentinel}")
private String authUsername;

@Value("${auth.password:sentinel}")
private String authPassword;

@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result login(HttpServletRequest request, String username, String password) {
if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) {
authUsername = DashboardConfig.getAuthUsername();
}

if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) {
authPassword = DashboardConfig.getAuthPassword();
}

/**
* If auth.username or auth.password is blank(set in application.properties or VM arguments),
* auth will pass, as the front side validate the input which can't be blank,
* so user can input any username or password(both are not blank) to login in that case.
*/
if ( StringUtils.isNotBlank(authUsername) && !authUsername.equals(username)
|| StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) {
LOGGER.error("Login failed: Invalid username or password, username=" + username + ", password=" + password);
return Result.ofFail(-1, "Invalid username or password");
}

AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username);
request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSTION_KEY, authUser);
return Result.ofSuccess(authUser);
}

@RequestMapping(value = "/logout", method = RequestMethod.POST)
public Result logout(HttpServletRequest request) {
request.getSession().invalidate();
return Result.ofSuccess(null);
}
}
Loading