optimized user auth

This commit is contained in:
Marvin Zhang
2019-07-27 14:55:03 +08:00
parent b0cce2f781
commit 92d443f19d
8 changed files with 121 additions and 45 deletions

View File

@@ -1,28 +1,53 @@
package middlewares
import (
"crawlab/constants"
"crawlab/routes"
"crawlab/services"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
func AuthorizationMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenStr := c.GetHeader("Authorization")
if c.Request.URL.Path == "/login" || (c.Request.URL.Path == "/users" && c.Request.Method == "PUT") {
// 如果为登录或注册,不用校验
if c.Request.URL.Path == "/login" ||
(c.Request.URL.Path == "/users" && c.Request.Method == "PUT") ||
strings.HasSuffix(c.Request.URL.Path, "download") {
c.Next()
} else {
_, err := services.CheckToken(tokenStr)
if err == nil {
c.Next()
} else {
return
}
// 获取token string
tokenStr := c.GetHeader("Authorization")
// 校验token
user, err := services.CheckToken(tokenStr)
// 校验失败,返回错误响应
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, routes.Response{
Status: "ok",
Message: "unauthorized",
Error: "unauthorized",
})
return
}
// 如果为普通权限,校验请求地址是否符合要求
if user.Role == constants.RoleNormal {
if strings.HasPrefix(strings.ToLower(c.Request.URL.Path), "/users") {
c.AbortWithStatusJSON(http.StatusUnauthorized, routes.Response{
Status: "ok",
Message: "unauthorized",
Error: "unauthorized",
})
return
}
}
// 校验成功
c.Next()
}
}

View File

@@ -2,6 +2,7 @@ package model
import (
"crawlab/database"
"crawlab/utils"
"github.com/apex/log"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
@@ -127,6 +128,12 @@ func UpdateUser(id bson.ObjectId, item User) error {
return err
}
if item.Password == "" {
item.Password = result.Password
} else {
item.Password = utils.EncryptPassword(item.Password)
}
if err := item.Save(); err != nil {
return err
}

View File

@@ -1,4 +1,5 @@
import axios from 'axios'
import router from '../router'
let baseUrl = process.env.VUE_APP_BASE_URL ? process.env.VUE_APP_BASE_URL : 'http://localhost:8000/api'
@@ -16,7 +17,13 @@ const request = (method, path, params, data) => {
headers
})
.then(resolve)
.catch(reject)
.catch(error => {
console.log(error)
if (error.response.status === 401) {
router.push('/login')
}
reject(error)
})
})
}

View File

@@ -10,11 +10,24 @@ const user = {
roles: [],
userList: [],
userForm: {},
userInfo: undefined,
adminPaths: [
'/users'
],
pageNum: 1,
pageSize: 10,
totalCount: 0
},
getters: {
userInfo (state) {
if (state.userInfo) return state.userInfo
const userInfoStr = window.localStorage.getItem('user_info')
if (!userInfoStr) return {}
return JSON.parse(userInfoStr)
}
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
@@ -34,6 +47,9 @@ const user = {
SET_USER_FORM: (state, value) => {
state.userForm = value
},
SET_USER_INFO: (state, value) => {
state.userInfo = value
},
SET_PAGE_NUM: (state, value) => {
state.pageNum = value
},
@@ -64,23 +80,13 @@ const user = {
},
// 获取用户信息
// getInfo ({ commit, state }) {
// return new Promise((resolve, reject) => {
// getInfo(state.token).then(response => {
// const data = response.data
// if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
// commit('SET_ROLES', data.roles)
// } else {
// reject(new Error('getInfo: roles must be a non-null array !'))
// }
// commit('SET_NAME', data.name)
// commit('SET_AVATAR', data.avatar)
// resolve(response)
// }).catch(error => {
// reject(error)
// })
// })
// },
getInfo ({ commit, state }) {
return request.get('/me')
.then(response => {
commit('SET_USER_INFO', response.data.data)
window.localStorage.setItem('user_info', JSON.stringify(response.data.data))
})
},
// 注册
register ({ dispatch, commit, state }, userInfo) {
@@ -99,6 +105,8 @@ const user = {
logout ({ commit, state }) {
return new Promise((resolve, reject) => {
window.localStorage.removeItem('token')
window.localStorage.removeItem('user_info')
commit('SET_USER_INFO', undefined)
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
resolve()

View File

@@ -15,13 +15,16 @@
</template>
<script>
import { mapGetters } from 'vuex'
import { mapState, mapGetters } from 'vuex'
import variables from '@/styles/variables.scss'
import SidebarItem from './SidebarItem'
export default {
components: { SidebarItem },
computed: {
...mapState('user', [
'adminPaths'
]),
...mapGetters([
'sidebar'
]),
@@ -30,8 +33,12 @@ export default {
return `/${pathArray[1]}`
},
routes () {
// console.log(this.$router.options.routes.filter(d => !d.hidden))
return this.$router.options.routes
return this.$router.options.routes.filter(d => {
const role = this.$store.getters['user/userInfo'].role
if (role === 'admin') return true
console.log(role)
return !this.adminPaths.includes(d.path)
})
},
variables () {
return variables

View File

@@ -22,7 +22,7 @@
name="password"
auto-complete="on"
:placeholder="$t('Password')"
@keyup.enter.native="isSignUp ? handleSignup : handleLogin"/>
@keyup.enter.native="onKeyEnter"/>
</el-form-item>
<el-form-item v-if="isSignUp" prop="confirmPassword" style="margin-bottom: 28px;">
<el-input
@@ -31,7 +31,7 @@
name="password"
auto-complete="on"
:placeholder="$t('Confirm Password')"
@keyup.enter.native="isSignUp ? handleSignup : handleLogin"
@keyup.enter.native="onKeyEnter"
/>
</el-form-item>
<el-form-item style="border: none">
@@ -105,13 +105,15 @@ export default {
confirmPassword: [{ required: true, trigger: 'blur', validator: validateConfirmPass }]
},
loading: false,
pwdType: 'password',
redirect: undefined
pwdType: 'password'
}
},
computed: {
isSignUp () {
return this.$route.path === '/signup'
},
redirect () {
return this.$route.query.redirect
}
},
methods: {
@@ -122,6 +124,7 @@ export default {
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.loading = false
this.$router.push({ path: this.redirect || '/' })
this.$store.dispatch('user/getInfo')
}).catch(() => {
this.$message.error(this.$t('Error when logging in (Please check username and password)'))
this.loading = false
@@ -142,6 +145,10 @@ export default {
})
}
})
},
onKeyEnter () {
const func = this.isSignUp ? this.handleSignup : this.handleLogin
func()
}
},
mounted () {

View File

@@ -95,7 +95,7 @@ export default {
this.$store.dispatch('task/getTaskResults', this.$route.params.id)
},
downloadCSV () {
window.location.href = this.$request.baseUrl + '/tasks/' + this.$route.params.id + '/download_results'
window.location.href = this.$request.baseUrl + '/tasks/' + this.$route.params.id + '/results/download'
this.$st.sendEv('任务详情-结果', '下载CSV')
}
},

View File

@@ -2,13 +2,13 @@
<div class="app-container">
<!--dialog-->
<el-dialog :visible.sync="dialogVisible" :title="$t('Edit User')">
<el-form label-width="80px">
<el-form ref="form" :model="userForm" label-width="80px" :rules="rules" inline-message>
<el-form-item :label="$t('Username')">
<el-input v-model="userForm.username" disabled></el-input>
</el-form-item>
<!--<el-form-item :label="$t('Password')">-->
<!--<el-input type="password" v-model="userForm.password" :placeholder="$t('Password')"></el-input>-->
<!--</el-form-item>-->
<el-form-item prop="password" :label="$t('Password')">
<el-input type="password" v-model="userForm.password" :placeholder="$t('Password')"></el-input>
</el-form-item>
<el-form-item :label="$t('Role')">
<el-select v-model="userForm.role">
<el-option value="admin" :label="$t('admin')"></el-option>
@@ -92,8 +92,19 @@ import dayjs from 'dayjs'
export default {
name: 'UserList',
data () {
const validatePass = (rule, value, callback) => {
if (!value) return callback()
if (value.length < 5) {
callback(new Error(this.$t('Password length should be no shorter than 5')))
} else {
callback()
}
}
return {
dialogVisible: false
dialogVisible: false,
rules: {
password: [{ validator: validatePass }]
}
}
},
computed: {
@@ -149,13 +160,17 @@ export default {
},
onConfirm () {
this.dialogVisible = false
this.$store.dispatch('user/editUser')
.then(() => {
this.$message({
type: 'success',
message: this.$t('Saved successfully')
})
})
this.$refs.form.validate(valid => {
if (valid) {
this.$store.dispatch('user/editUser')
.then(() => {
this.$message({
type: 'success',
message: this.$t('Saved successfully')
})
})
}
})
this.$st.sendEv('用户', '编辑')
}
},