上一篇写了下若依前后端分离框架中前端至弹出登陆界面的过程,本片来详细了解下登录的整个过程。
    后端包含ruoyi-admin,ruoyi-common,ruoyi-framework等多个模块,ruoyi-admin为启动模块。先看一下ruoyi-admin/src/main/application.yml配置文件。

# 开发环境配置
server:# 服务器的HTTP端口,默认为8080port: 8080

指定了服务端启动的端口8080。我们运行ruoyi-admin/src/main/java/com/ruoyi/
RuoYiApplication.java即可启动后端,监听8080端口。
    我们回到前端的登录界面。 views/login.vue

<template><div class="login"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"><h3 class="title">若依后台管理系统</h3><el-form-item prop="username"><el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号"><svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="password"><el-inputv-model="loginForm.password"type="password"auto-complete="off"placeholder="密码"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" /></el-input></el-form-item><el-form-item prop="code"><el-inputv-model="loginForm.code"auto-complete="off"placeholder="验证码"style="width: 63%"@keyup.enter.native="handleLogin"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="login-code"><img :src="codeUrl" @click="getCode" class="login-code-img"/></div></el-form-item><el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleLogin"><span v-if="!loading">登 录</span><span v-else>登 录 中...</span></el-button></el-form-item></el-form><!--  底部  --><div class="el-login-footer"><span>Copyright © 2018-2020 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'export default {name: "Login",data() {return {codeUrl: "",cookiePassword: "",loginForm: {username: "admin",password: "admin123",rememberMe: false,code: "",uuid: ""},loginRules: {username: [{ required: true, trigger: "blur", message: "用户名不能为空" }],password: [{ required: true, trigger: "blur", message: "密码不能为空" }],code: [{ required: true, trigger: "change", message: "验证码不能为空" }]},loading: false,redirect: undefined};},watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect;},immediate: true}},created() {this.getCode();this.getCookie();},methods: {getCode() {getCodeImg().then(res => {this.codeUrl = "data:image/gif;base64," + res.img;this.loginForm.uuid = res.uuid;});},getCookie() {const username = Cookies.get("username");const password = Cookies.get("password");const rememberMe = Cookies.get('rememberMe')this.loginForm = {username: username === undefined ? this.loginForm.username : username,password: password === undefined ? this.loginForm.password : decrypt(password),rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)};},handleLogin() {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true;if (this.loginForm.rememberMe) {Cookies.set("username", this.loginForm.username, { expires: 30 });Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });} else {Cookies.remove("username");Cookies.remove("password");Cookies.remove('rememberMe');}this.$store.dispatch("Login", this.loginForm).then(() => {this.$router.push({ path: this.redirect || "/" });}).catch(() => {this.loading = false;this.getCode();});}});}}
};
</script>

页面加载前,created()调用this.getCode()和this.getCookie()获取验证码和cookie。这里不详细说明。我们重点关注下handleLogin登录函数。this.$refs.loginForm.validate判断用户名、密码和验证码的合法性。this.loginForm.rememberMe判断是否选择了记住密码。如果记住密码,我们把用户名密码存进cookie,下次跳到登录界面就不需要输用户名密码。如果不选择记住密码,将清空cookie,下次跳到登录界面需要手动输入用户名和密码。
    this.$store.dispatch(“Login”, this.loginForm),这里是我们最需要关注的地方。调用store里的Login对应的函数,把loginForm作为参数。我们跳到src/store/modules/user.js。

import { login, logout, getInfo } from '@/api/login'
......
actions: {// 登录Login({ commit }, userInfo) {const username = userInfo.username.trim()const password = userInfo.passwordconst code = userInfo.codeconst uuid = userInfo.uuidreturn new Promise((resolve, reject) => {login(username, password, code, uuid).then(res => {setToken(res.token)commit('SET_TOKEN', res.token)resolve()}).catch(error => {reject(error)})})},......}

这里新建了一个Promise,调用了login函数,login函数是从@/api/login引入的。我们再跳到src/api/login.js看一下这个函数:

// 登录方法
export function login(username, password, code, uuid) {const data = {username,password,code,uuid}return request({url: '/login',method: 'post',data: data})
}

这里构建了一个ajax的POST请求,url为/login,data为用户名、密码、验证码的结构数据。我们再简单看一下request对应的src/utils/request.js。

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({// axios中请求配置有baseURL选项,表示请求URL公共部分baseURL: process.env.VUE_APP_BASE_API,// 超时timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {// 是否需要设置 tokenconst isToken = (config.headers || {}).isToken === falseif (getToken() && !isToken) {config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改}return config
}, error => {console.log(error)Promise.reject(error)
})

我们看到请求的地址是process.env.VUE_APP_BASE_API, 这是怎么访问到后端的的呢?我们回头看一下vue.config.js文件

devServer: {host: '0.0.0.0',port: port,open: true,proxy: {// detail: https://cli.vuejs.org/config/#devserver-proxy[process.env.VUE_APP_BASE_API]: {target: `http://localhost:8080`,changeOrigin: true,pathRewrite: {['^' + process.env.VUE_APP_BASE_API]: ''}}},disableHostCheck: true},

这里做了一个代理,把process.env.VUE_APP_BASE_API地址映射成了http://localhost:8080。所以我们login最终访问后端的地址就变为http://localhost:8080/login。
    现在看一下后端接收这个请求的代码。ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java。

/*** 登录方法* * @param loginBody 登录信息* @return 结果*/@PostMapping("/login")public AjaxResult login(@RequestBody LoginBody loginBody){AjaxResult ajax = AjaxResult.success();// 生成令牌String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),loginBody.getUuid());ajax.put(Constants.TOKEN, token);return ajax;}

接收到/login的POST请求后,调用loginService.login方法进行验证,并生成token返回给客户端。接下来,看一下loginService.login方法。

 /*** 登录验证* * @param username 用户名* @param password 密码* @param code 验证码* @param uuid 唯一标识* @return 结果*/public String login(String username, String password, String code, String uuid){......// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));......LoginUser loginUser = (LoginUser) authentication.getPrincipal();// 生成tokenreturn tokenService.createToken(loginUser);}

这里牵扯到Spring Security 的知识,我们看一下Spring Security的配置ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java。

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{ /*** 身份认证接口*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());}
}

这里设置了userDetailService。UserDetailsServiceImpl实现了UserDetailService接口,实现了loadUserByUsername方法。我们再看一下UserDetailsServiceImpl.loadUserByUsername的实现:

@Service
public class UserDetailsServiceImpl implements UserDetailsService
{@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{SysUser user = userService.selectUserByUserName(username);if (StringUtils.isNull(user)){log.info("登录用户:{} 不存在.", username);throw new UsernameNotFoundException("登录用户:" + username + " 不存在");}else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info("登录用户:{} 已被删除.", username);throw new BaseException("对不起,您的账号:" + username + " 已被删除");}else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info("登录用户:{} 已被停用.", username);throw new BaseException("对不起,您的账号:" + username + " 已停用");}return createLoginUser(user);}}

就是根据用户名去数据库查询用户信息并返回。然后 再调用Spring Security内部的认证代码进行认证。

protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials() == null) {this.logger.debug("Authentication failed: no credentials provided");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));} else {String presentedPassword = authentication.getCredentials().toString();if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug("Authentication failed: password does not match stored value");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}}}

就是比较根据用户名获取到的用户的密码和认证信息参数中的密码进行比较。更为详细的过程,这里引用了一位大神的文章,介绍的很详细。
https://blog.csdn.net/yuanlaijike/article/details/84703690
认证完成后,根据用户的信息,构建token,进行返回。前端收到返回后,存储token,并跳转网页。this.$router.push({ path: this.redirect || “/” });

    大体的登陆过程就是这样子。
    上一章,若依前后端分离框架学习-1:前端引入
    下一章,若依前后端分离框架学习-3:获取菜单
    返回目录

若依前后端分离框架学习-2:登录过程相关推荐

  1. 若依前后端分离框架去掉首页 登录后跳转至动态路由的第一个路由

    若依前后端分离框架去掉首页 登录后跳转至动态路由的第一个路由 若依框架VUE前端界面,登录后默认跳转至动态路由第一路由(第一个子菜单) 一.登录后跳转第一路由界面 二.设置路由的首页路径,方便后续的获 ...

  2. 若依前后端分离框架学习-6:日志管理

    这一章,我们来学习下若依前后端分离框架中的日志管理.学习自定义注解的使用.     我们编辑一下"测试子模块"中张三这条数据.然后查看下操作日志.     这里我们看到,编辑的日志 ...

  3. 若依前后端分离框架学习-4:创建模块

    前面三章,介绍了下若依前后端分离框架的登录过程中前端和后端的代码流程.本章,我们学习下如何创建自己的业务模块. 第一步,右键后端项目名称,新建模块 第二步,选择Maven项目 第三步,填写项目名称 第 ...

  4. 若依前后端分离框架学习-3:获取菜单

    前一章,描述了用户登录的过程.这一章,来写一下,用户登录成功后,如何根据权限,渲染出菜单的. 我们来看下,前端根目录对应的路由信息. {path: '',component: Layout,redir ...

  5. 若依前后端分离框架学习-5:权限管理

    上一章自己创建了一个模块,我们注意到前端代码中对于按钮有v-hasPermi="['xxx:testxxx:add']"这种代码.服务端有@PreAuthorize("@ ...

  6. 搭建spring-boot+vue前后端分离框架并实现登录功能

    一.环境.工具 jdk1.8 maven spring-boot idea VSVode vue 二.搭建后台spring-boot框架 步骤: 1.new- project选择Spring Init ...

  7. Angular4+AdminLTE+Jeecg 前后端分离框架实战-张代浩-专题视频课程

    Angular4+AdminLTE+Jeecg 前后端分离框架实战-2259人已学习 课程介绍         Angular4+AdminLTE+Jeecg 前后端分离框架实战 涉及技术点:angu ...

  8. vue-element-admin/template+tornado(pyrestful)前后端分离框架实践(1)——自定义菜单和仪表盘

    0. 写在前面 vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现.它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证, ...

  9. Python Web前后端分离框架Django+Vue搭建

    Python Web前后端分离框架Django+Vue搭建 对前面所学知识的归纳整理,感兴趣的可以看看,欢迎指正. 一.前后端分离框架介绍 本项目基于 Python 的 Web 框架开发,采用前后端分 ...

最新文章

  1. tensorflow1.0代码迁移到2.0官方指导
  2. oracle 学习小结1
  3. Git创建版本库详尽教程
  4. Objective-C 注释规范心得
  5. Java与.NET的WebServices相互调用
  6. mac mysql5.7.10 密码_MAC下Mysql5.7.10版本修改root密码的方法
  7. 天地图专题一:加载天地图
  8. VMware 虚拟机桥接网络设置
  9. 首周任务 :asp留言板
  10. oppok3如何刷机_OPPO K3怎么刷机?
  11. 计算机桌面桌面设置动态视频教程,如何在计算机上设置动态桌面?电脑设置动态视频桌面图形教程...
  12. 利用简单的磁盘映射,Samba帮助网站与关关采集器分离,实现杰奇小说远程采集,弊端很多,不建议使用,建议直接利用桌面安装
  13. psql计算环比和同比
  14. uniapp基本语法/组件使用
  15. 网络 - VXLAN
  16. 使用FontCreator打造属于自己的字体
  17. Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?...
  18. DeepSORT论文翻译(SIMPLE ONLINE AND REALTIME TRACKING WITH A DEEP ASSOCIATION METRIC)
  19. C文件中的中文乱码 notepad2
  20. x265-1.8版本-common/framedata.h注释

热门文章

  1. 修复损坏的gzip压缩文件原理和修复方法
  2. 基于Opencv的图片人脸检测
  3. 《精通CFD工程仿真与案例实战---FLUENT GAMBIT ICEM CFD Tecplot(第2版)》—— 导读...
  4. 如何设计好软硬结合板的小技巧来啦~ 22021/11/22
  5. Multi-Objective Data Placement for Multi-Cloud Socially Aware Services---INFOCOM 2014
  6. 面试题【jdk8新特性】
  7. BIM Revit2017下载安装教程及安装包
  8. Flash 实验 遮罩层
  9. 【使用VS2022写c语言】第一次使用VS2022写c语言代码可能会遇到的问题及解决方法。
  10. Android Studio中 HAXM安装失败的问题(Intel HAXM installation failed. To install Intel HAXM follow the...)