This is an automated email from the ASF dual-hosted git repository. guangning pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/pulsar-manager.git
The following commit(s) were added to refs/heads/master by this push: new 4ab8811 Add support for casdoor (#446) 4ab8811 is described below commit 4ab8811a48ce7d2c8148f765f9cd9b62fbb76bfb Author: Fabian Bao <baoyaol...@gmail.com> AuthorDate: Wed Apr 13 09:54:09 2022 +0800 Add support for casdoor (#446) feet: add support for casdoor --- build.gradle | 1 + front-end/package.json | 4 +-- front-end/src/api/login.js | 13 +++++++++ front-end/src/lang/en.js | 1 + front-end/src/lang/zh.js | 1 + front-end/src/permission.js | 2 +- front-end/src/router/index.js | 7 ++++- front-end/src/store/modules/user.js | 18 ++++++++++++ front-end/src/views/callback/index.vue | 18 ++++++++++++ front-end/src/views/login/index.vue | 13 +++++++-- .../pulsar/manager/controller/LoginController.java | 34 ++++++++++++++++++++++ .../manager/interceptor/WebAppConfigurer.java | 1 + src/main/resources/application.properties | 12 ++++++-- 13 files changed, 117 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 0ccab9c..9a9dc9b 100644 --- a/build.gradle +++ b/build.gradle @@ -148,6 +148,7 @@ dependencies { compile group: 'org.glassfish.jersey.media', name: 'jersey-media-json-jackson', version: jerseyVersion compile group: 'org.springframework.boot', name: 'spring-boot-starter-security' compile group: 'org.springframework.security', name: 'spring-security-config' + compile group: 'org.casbin', name: 'casdoor-spring-boot-starter', version: '1.2.0' compileOnly group: 'org.projectlombok', name: 'lombok', version: lombokVersion compileOnly group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springBootVersion testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion diff --git a/front-end/package.json b/front-end/package.json index 64225d6..eca0935 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -5,7 +5,7 @@ "author": "<eguangn...@gmail.com>", "license": "Apache License 2.0", "scripts": { - "dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "dev": "cross-env BABEL_ENV=development NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", "build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js", "lint": "eslint --ext .js,.vue src", @@ -35,6 +35,7 @@ }, "dependencies": { "axios": "0.18.1", + "casdoor-js-sdk": "^0.2.5", "clipboard": "1.7.1", "codemirror": "5.39.2", "connect": "3.6.6", @@ -97,7 +98,6 @@ "request": "^2.88.0", "rimraf": "2.6.2", "sass-loader": "7.0.3", - "script-ext-html-webpack-plugin": "2.0.1", "script-loader": "0.7.2", "semver": "5.5.0", "serve-static": "1.13.2", diff --git a/front-end/src/api/login.js b/front-end/src/api/login.js index 0c3ca2e..7bfc6f4 100644 --- a/front-end/src/api/login.js +++ b/front-end/src/api/login.js @@ -26,6 +26,19 @@ export function loginByUsername(username, password) { }) } +export function loginByCasdoor(code, state) { + const data = { + code, + state + } + return request({ + headers: { 'Content-Type': 'application/json' }, + url: '/pulsar-manager/casdoor', + method: 'post', + data + }) +} + export function logout() { return request({ url: '/pulsar-manager/logout', diff --git a/front-end/src/lang/en.js b/front-end/src/lang/en.js index 6e359da..1161a62 100644 --- a/front-end/src/lang/en.js +++ b/front-end/src/lang/en.js @@ -104,6 +104,7 @@ export default { username: 'Username', password: 'Password', any: 'any', + casdoor: 'Login with casdoor', thirdparty: 'Or connect with', thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !' }, diff --git a/front-end/src/lang/zh.js b/front-end/src/lang/zh.js index d466f8b..610d2ba 100644 --- a/front-end/src/lang/zh.js +++ b/front-end/src/lang/zh.js @@ -104,6 +104,7 @@ export default { username: 'Username', password: 'Password', any: 'any', + casdoor: 'Login with casdoor', thirdparty: 'Or connect with', thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !' }, diff --git a/front-end/src/permission.js b/front-end/src/permission.js index 42b574e..b100d90 100644 --- a/front-end/src/permission.js +++ b/front-end/src/permission.js @@ -27,7 +27,7 @@ function hasPermission(roles, permissionRoles) { return roles.some(role => permissionRoles.indexOf(role) >= 0) } -const whiteList = ['/login']// no redirect whitelist +const whiteList = ['/login', '/callback']// no redirect whitelist router.beforeEach((to, from, next) => { NProgress.start() // start progress bar diff --git a/front-end/src/router/index.js b/front-end/src/router/index.js index 64f6339..e7be854 100644 --- a/front-end/src/router/index.js +++ b/front-end/src/router/index.js @@ -54,6 +54,11 @@ export const constantRouterMap = [ component: () => import('@/views/login/index'), hidden: true }, + { + path: '/callback', + component: () => import('@/views/callback/index'), + hidden: true + }, { path: '/auth-redirect', component: () => import('@/views/login/authredirect'), @@ -72,7 +77,7 @@ export const constantRouterMap = [ ] export default new Router({ - // mode: 'history', // require service support + mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRouterMap }) diff --git a/front-end/src/store/modules/user.js b/front-end/src/store/modules/user.js index b2acd3c..9b2dc5d 100644 --- a/front-end/src/store/modules/user.js +++ b/front-end/src/store/modules/user.js @@ -19,6 +19,7 @@ import { removeCsrfToken } from '@/utils/csrfToken' import { Message } from 'element-ui' import { setTenant, removeTenant } from '../../utils/tenant' import { getUserInfo } from '@/api/users' +import { loginByCasdoor } from '../../api/login' const user = { state: { @@ -86,6 +87,23 @@ const user = { }) }, + LoginByCasdoor({ commit }, code, state) { + return new Promise((resolve, reject) => { + loginByCasdoor(code, state).then(response => { + if (response.data.hasOwnProperty('error') && response.data.error.length >= 0) { + reject('login error') + } + commit('SET_TOKEN', response.headers.token) + setToken(response.headers.token) + setName(response.headers.username) + setTenant(response.headers.tenant) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + // 获取用户信息 GetUserInfo({ commit, state }) { return new Promise((resolve, reject) => { diff --git a/front-end/src/views/callback/index.vue b/front-end/src/views/callback/index.vue new file mode 100644 index 0000000..7e1068e --- /dev/null +++ b/front-end/src/views/callback/index.vue @@ -0,0 +1,18 @@ +<script> +export default { + name: 'Index', + watch: { + $route: { + handler: function(route) { + this.$store.dispatch('LoginByCasdoor', route.query.code, route.query.state).then(() => { + this.$router.push({ path: '/' }) + }).catch(() => { + console.log('login error!!') + }) + }, + immediate: true + } + + } +} +</script> diff --git a/front-end/src/views/login/index.vue b/front-end/src/views/login/index.vue index f4b79c3..cf0aa59 100644 --- a/front-end/src/views/login/index.vue +++ b/front-end/src/views/login/index.vue @@ -53,9 +53,10 @@ </el-form-item> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">{{ $t('login.logIn') }}</el-button> + <el-button :loading="loading" type="primary" style="width:100%;margin-left:0;" @click="loginWithCasdoor">{{ $t('login.casdoor') }}</el-button> <!-- <el-button class="thirdparty-button" type="primary" @click="showDialog=true"> - Or connect with - </el-button> --> + Or connect with + </el-button> --> </el-form> <el-dialog :title="$t('login.thirdparty')" :visible.sync="showDialog" append-to-body> @@ -180,6 +181,14 @@ export default { getCsrfToken().then(response => { setCsrfToken(response.headers['x-csrf-token']) }) + }, + loginWithCasdoor() { + window.location.href = 'http://localhost:7001/login/oauth/authorize' + + '?client_id=6ba06c1e1a30929fdda7' + + '&response_type=code' + + '&redirect_uri=http://localhost:9527/callback' + + '&scope=read' + + '&state=pulsar' } } } diff --git a/src/main/java/org/apache/pulsar/manager/controller/LoginController.java b/src/main/java/org/apache/pulsar/manager/controller/LoginController.java index 7e199ee..febf877 100644 --- a/src/main/java/org/apache/pulsar/manager/controller/LoginController.java +++ b/src/main/java/org/apache/pulsar/manager/controller/LoginController.java @@ -30,6 +30,10 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import org.apache.pulsar.manager.utils.ResourceType; +import org.casbin.casdoor.entity.CasdoorUser; +import org.casbin.casdoor.service.CasdoorAuthService; +import org.casbin.casdoor.service.CasdoorResourceService; +import org.casbin.casdoor.service.CasdoorUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; @@ -81,6 +85,9 @@ public class LoginController { @Autowired private RolesRepository rolesRepository; + @Autowired + private CasdoorAuthService casdoorAuthService; + @Autowired private RoleBindingRepository roleBindingRepository; @@ -180,4 +187,31 @@ public class LoginController { jwtService.removeToken(request.getSession().getId()); return ResponseEntity.ok(result); } + + @ApiOperation(value = "Logout pulsar manager") + @ApiResponses({ + @ApiResponse(code = 200, message = "ok"), + @ApiResponse(code = 500, message = "Internal server error") + }) + @RequestMapping(value = "/casdoor", method = RequestMethod.POST) + public ResponseEntity<Map<String, Object>> callback( + @RequestBody Map<String, String> body) { + Map<String, Object> result = Maps.newHashMap(); + String code = body.get("code"); + String state = body.get("state"); + String token = casdoorAuthService.getOAuthToken(code, state); + if(!token.startsWith("error")){ + result.put("error", token.substring(7)); + return ResponseEntity.ok(result); + } + result.put("login", "success"); + HttpHeaders headers = new HttpHeaders(); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String jwtToken = jwtService.toToken(account + "-" + password); + jwtService.setToken(request.getSession().getId(), jwtToken); + headers.add("token", jwtToken); + headers.add("tenant","pulsar"); + headers.add("username", "pulsar"); + return new ResponseEntity<>(result, headers, HttpStatus.OK); + } } diff --git a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java index ddfd79f..26adffc 100644 --- a/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java +++ b/src/main/java/org/apache/pulsar/manager/interceptor/WebAppConfigurer.java @@ -34,6 +34,7 @@ public class WebAppConfigurer implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(adminHandlerInterceptor).addPathPatterns("/**") .excludePathPatterns("/pulsar-manager/login") + .excludePathPatterns("/pulsar-manager/casdoor") .excludePathPatterns("/pulsar-manager/users/superuser") .excludePathPatterns("/pulsar-manager/csrf-token") .excludePathPatterns("/pulsar-manager/third-party-login/**") diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ba598eb..2781e4e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -88,7 +88,7 @@ jwt.sessionTime=2592000 pulsar-manager.account=pulsar pulsar-manager.password=pulsar # If true, the database is used for user management -user.management.enable=true +user.management.enable=false # Optional -> SECRET, PRIVATE, default -> PRIVATE, empty -> disable auth # SECRET mode -> bin/pulsar tokens create --secret-key file:///path/to/my-secret.key --subject test-user @@ -154,5 +154,13 @@ tls.pulsar.admin.ca-certs=ca-client-path # support peek message, default false pulsar.peek.message=false -# swagger configration +# swagger configuration swagger.enabled=false + +# casdoor configuration +casdoor.endpoint = http://localhost:8000 +casdoor.clientId = 6ba06c1e1a30929fdda7 +casdoor.clientSecret = df92bbf913225ebbae9af7ba8d41fe19507eb079 +casdoor.jwtPublicKey = -----BEGIN CERTIFICATE-----\nMIIE+TCCAuGgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMDYxHTAbBgNVBAoTFENh\nc2Rvb3IgT3JnYW5pemF0aW9uMRUwEwYDVQQDEwxDYXNkb29yIENlcnQwHhcNMjEx\nMDE1MDgxMTUyWhcNNDExMDE1MDgxMTUyWjA2MR0wGwYDVQQKExRDYXNkb29yIE9y\nZ2FuaXphdGlvbjEVMBMGA1UEAxMMQ2FzZG9vciBDZXJ0MIICIjANBgkqhkiG9w0B\nAQEFAAOCAg8AMIICCgKCAgEAsInpb5E1/ym0f1RfSDSSE8IR7y+lw+RJjI74e5ej\nrq4b8zMYk7HeHCyZr/hmNEwEVXnhXu1P0mBeQ5ypp/QGo8vgEmjAETNmzkI1NjOQ\nCjCYwUrasO/f/MnI1C0j13vx6mV1kHZjSrKsMhYY1vax [...] +casdoor.organizationName = built-in +casdoor.applicationName = pulsar