计算机专业网站开发方向扬中网站推广哪家好

当前位置: 首页 > news >正文

计算机专业网站开发方向,扬中网站推广哪家好,招聘网站的SEO怎么做,游戏源代码网站一、登录功能的前端处理过程 1、导入项目所需的图片和CSS等静态文件 参考代码存放client节点的/opt/code目录下 执行如下命令#xff1a; [rootclient ~]# cp -r /opt/code/kongguan_web/src/assets/* /root/kongguan_web/src/assets/ 将参考代码中的css、icon、images等文…一、登录功能的前端处理过程 1、导入项目所需的图片和CSS等静态文件 参考代码存放client节点的/opt/code目录下 执行如下命令 [rootclient ~]# cp -r /opt/code/kongguan_web/src/assets/* /root/kongguan_web/src/assets/ 将参考代码中的css、icon、images等文件夹或文件直接拷贝到当前创建的新项目的src/assets/目录中 2、完成项目中使用的工具类的编写此处的代码可由学生独立完成也可以由老师提供直接导入到项目中 编写或直接导入工具类文件所有的工具类都放在src/utils目录下 src/utils/message.js消息弹窗管理src/utils/request.js用于请求的身份验证 在src/utils/message.js中定义了消息弹窗管理代码如下 import { Message } from element-ui; let messageInstance null; let mainMessage function DoneMessage(options) {//如果弹窗已存在先关闭if (messageInstance) {messageInstance.close();}messageInstance Message(options); } let arr [success, warning, info, error]; arr.forEach(function (type) {mainMessage[type] function (options) {if (typeof options string) {options {message: options};}options.type type;return mainMessage(options);}; }); export const message mainMessage; 在src/utils/request.js中定义了拦截器用于拦截“请求”和“响应”进行身份验证代码如下 import Vue from vue import axios from axiosVue.prototype.\(ajax Vue.ajax axiosaxios.defaults.baseURL process.env.NODE_ENV ! production ? : (process.env.VUE_APP_BASE_API)axios.interceptors.request.use(config {if (localStorage.getItem(Authorization)) {config.headers.common[Authorization] localStorage.getItem(Authorization);} else {}return config; }, err {return Promise.reject(err); }) axios.interceptors.response.use(function (response) {// console.log(response);// console.log(response.headers.Authorization);// 对响应数据做点什么return response.data; }, function (error) {if (JSON.stringify(error).indexOf(401) ! -1) { // token失效localStorage.removeItem(Authorization)Vue.prototype.\)message.error(token过期)Vue.prototype.\(router.push({path: /login,})} else if (JSON.stringify(error).indexOf(403) ! -1) {localStorage.removeItem(Authorization)Vue.prototype.\)message.error(token无效)Vue.prototype.\(router.push({path: /login,})}// 对响应错误做点什么return Promise.reject(error); });export default axios; 3、编写 src/api/login/login.js文件向服务端发送请求实现登录 定义login方法使用POST请求向服务端发送表单数据data服务端返回的数据包括登录状态、身份token、权限等信息。 import request from ../../utils/requestconst baseUrl/api/*** 用户登录*/ export function login(data){return request({url:baseUrl/login,method:post,data:data}) } 4、编写src/store/index.js用于Token的存储 src/store/index.js文件用于存储token修改token并将token存入localStorage import Vue from vue import Vuex from vuexVue.use(Vuex) export default new Vuex.Store({state: {// 存储tokenAuthorization: localStorage.getItem(Authorization) ? localStorage.getItem(Authorization) : ,},mutations: {// 修改token并将token存入localStoragechangeLogin (state, user) {state.Authorization user.Authorization;localStorage.setItem(Authorization, user.Authorization);console.log(state.Authorization);}} }) 5、创建登录的Vue视图组件Login.vue 登录页面设计在src/views目录下创建Login目录然后在Login目录下创建Login.vue 登录页面 templatediv classlogindiv classtop_logo/div!-- 登录框区域 --div classform_boxdiv stylecolor: #f0f0f0;大数据航空案例/divel-form stylemargin-top: 60px :modelloginForm status-icon :rulesrules refloginFormel-row typeflex justifyleft :gutter20el-col :span3 aligncenterimg src../../assets/images/user-icon.png stylemargin-top: 3px//el-colel-col :span21 aligncenterel-form-item propaccountel-input classel-input__inner1 v-modelloginForm.account placeholder请输入用户名 maxlength20/el-input/el-form-item/el-col/el-rowel-row typeflexdiv classtableTitle//el-rowel-row typeflex justifyleft stylemargin-top: 50px :gutter20el-col :span3 aligncenterimg src../../assets/images/pwd-icon.png stylemargin-top: 3px//el-colel-col :span21el-form-item proppasswordel-input typepassword v-modelloginForm.password placeholder请输入密码 maxlength16/el-input/el-form-item/el-col/el-rowel-row typeflexdiv classtableTitle//el-rowel-form-item aligncenter stylemargin-top: 100pxel-button classel-button1 typeprimary; clicksubmitForm(loginForm)登 录/el-button/el-form-item/el-form/div!-- 底部区域 --/div /template ... 接下页... 初始化表单数据并定义表单验证规则例如如果用户名输入框为空则提示“请输入登陆账号” ... 接上页...scriptimport {mapMutations} from vuex;import {login} from /api/login/login;export default {data() {return {loginForm: {account: ,password: ,},otherQuery: {},rules: {account: [{required: true, message: 请输入登陆账号, trigger: blur}],password: [{required: true, message: 请输入登陆密码, trigger: blur}]}};}, ... 接下页... 处理请求的重定向地址获取请求的地址登录后直接跳转到请求的地址 其中getOtherQuery(query)方法是在下面的代码中定义的方法返回上一次请求的地址登录成功后直接跳转到该地址。 ... 接上页...watch: {\)route: {handler: function (route) {const query route.queryif (query) {this.redirect query.redirectthis.otherQuery this.getOtherQuery(query)}},immediate: true}}, … 接下页… 提交表单的过程如下 调用src/api/Login/Login.js中的login方法向服务端发送请求登录成功后将用户token和权限保存到本地vuex中然后跳转到上一次请求的地址。 … 接上页…//methods: {…mapMutations([changeLogin]),submitForm(formName) {this.\(refs[formName].validate(valid {if (valid) {login(this.loginForm).then(data {if (data.successful) {console.log(登录成功);// 将用户token保存到vuex中// this.changeLogin({Authorization: data.result.token});//localStorage.setItem(account, data.result.name)if (data.resultValue.Authorization ! null) {localStorage.setItem(Authorization, data.resultValue.Authorization);}if (data.resultValue.userAuth ! null) {localStorage.setItem(userAuth, data.resultValue.userAuth);}this.\)router.push({path: this.redirect || /, query: this.otherQuery});console.log(跳转);} else {console.log(登录失败);this.\(message.error(登录失败);}});} else {return false;}});},//获取请求的地址登录后直接跳转到请求的地址getOtherQuery(query) {return Object.keys(query).reduce((acc, cur) {if (cur ! redirect) {acc[cur] query[cur]}return acc}, {})}}}; /script ... 接下页... 页面样式如下 ... 接上页... style scoped.login {background: url(../../assets/images/loginBg.png) no-repeat;background-size: cover;height: 100%;}.top_logo {height: 130px;}.form_box {text-align: center;width: 550px;margin: 0 auto;}.login-footer {width: 100%;text-align: center;color: #ffffff;position:fixed;bottom:0;margin-bottom: 64px;size: 23px;padding-top: 100px;}.el-button1{background: #0376bf;border-color: #0376bf;width: 100%;color: #f0f0f0;height: 62px;}.tableTitle {margin: 0 auto;margin-top: 10px;width: 550px;height: 1px;background-color: #d4d4d4;} /style style.el-form-item {margin-bottom: 1px;}.el-form-item__error{margin-left: 15px;margin-top: 20px;}.el-input__inner {background: transparent;border: 0;color: #f0f0f0;} /style 6、创建登录成功后的主页面的Vue视图组件src/views/Home/Index.vue 由于当前任务是实现登录功能所以登录成功后的src/views/Home/Index.vue页面只显示简单的内容即可详细展示会在后续任务中完成。 templatediv stylecolor: #000000;大数据航空案例/div /templatescriptexport default{} /scriptstyle /style 7、为了实现页面风格的统一所以这里还需要创建一个src/views/Layout/Layout.vue和一个src/views/Layout/Header.vue 布局视图组件 除登录页面外其他显示的页面都嵌套在一个Layout.vue内且都显示同一个头部内容Header.vue。由于这是在后续任务中完成的工作所以这里只显示简单的内容即可。 src/views/Layout/Layout.vue templatediv classmainHeader/Headerdiv classcommon-rightrouter-view//div/div /templatescriptexport default{} /scriptstyle /style src/views/Layout/Header.vue templatediv stylecolor: red;大数据航空案例/div /templatescriptexport default{} /scriptstyle /style 8、编写src/router/index.js路由文件当我们访问任何页面时如果没有身份认证信息则会跳转到Login.vue 通过vue-router重写路由的push方法解决相同路径跳转报错的问题 import Vue from vue import Router from vue-routerconst originalPush Router.prototype.push; Router.prototype.push function push(location) {return originalPush.call(this, location).catch(err err) } ... 接下文 ... 设置基本路由规则 ... 接上文 ... Vue.use(Router) /* Layout */ import Layout from /views/Layout/Layout const router new Router({base: process.env.BASE_URL,mode: history,routes: [{path: /login,component: resolve require([/views/Login/Login], resolve),hidden: true,meta: {auth: true}},{path: /,component: Layout, redirect: /home,children: [{path: home,component: resolve require([/views/Home/Index], resolve),name: home,meta: { title: home }}]},] }) ... 接下文 ... 导航守卫使用router.beforeEach注册一个全局前置守卫用于获取Token来判断用户是否登陆如果没有登录则跳转到/Login.vue页面 ... 接上文 ... router.beforeEach((to, from, next) {if (to.path /login) {next();} else {let token localStorage.getItem(Authorization);if (token null || token ) {next(/login);} else {next();}} }); export default router 9、编写src/main.js文件该文件是应用系统入口、主方法 在src/main.js中导入  src/store/index 等文件有些组件如ECharts报表组件在当前任务中没有使用可以先导入暂不使用 import Vue from vue import App from ./App import router from ./router import store from ./store/index import ./assets/css/basic.css import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css; import ./assets/icon/iconfont.css; import * as echarts from echarts; import axios from axios; import moment from moment; import { message } from ./utils/message ... 略接下文 ... 定义全局属性以便可以在项目的任意位置使用 ... 略接上文 ... Vue.prototype.\)echarts echarts; Vue.prototype.\(axios axios; Vue.config.productionTip false Vue.use(ElementUI); Vue.prototype.\)message message; Vue.prototype.\(moment moment;new Vue({router,store,render:h h(App) }).\)mount(#app) … 略接下文 … 定义全局响应(response)拦截器和请求(request)拦截器 … 略接上文 … //定义一个响应拦截器 axios.interceptors.response.use(function (config) {let status config.code;//401未登录跳转到登录页if (status 401) {console.log(401);router.push(/login);}//403无权限跳转到登录页if (status 403) {console.log(403);router.push(/login);}return config })// request拦截器 axios.interceptors.request.use(config {// 如果想请求可以重复发起给在请求参数中加allowedRepeattrue (后续会删除不会发送给服务端)if (!config.data || !config.data.allowedRepeat) { // 如果不允许重复请求开启拦截// todo: 1. 设置拦截 防止重复请求// 拦截重复请求(即当前正在进行的相同请求)const requestData getRequestIdentify(config)removePending(requestData, true)// 使用 cancel token 取消请求 参考http://www.axios-js.com/zh-cn/docs/#%E6%8B%A6%E6%88%AA%E5%99%A8config.cancelToken new CancelToken(© {pending[requestData] c})} else { // 允许重复请求不进行拦截delete config.data.allowedRepeat // 把自定义的请求参数给删掉不发送给服务端}return config }, error {// Do something with request errorconsole.log(error) // for debugPromise.reject(error) }) … 略接下文 … 拦截重复请求 … 略接上文 … // 拦截重复请求 let pending {} const CancelToken axios.CancelToken // 请求标识完成请求后也需要执行删除记录所以添加此参数避免执行无用操作 const removePending (key, isRequest false) {if (pending[key] isRequest) {pendingkey}delete pending[key] } /*** 由于我们请求用了代理 直接代理到测试服务器 因此请求响应拦截器的config.url是一致的不需要标识值区分* 如果请求拦截器和响应拦截器的config.url不一致就需要一个标识值用来区分判断/ const getRequestIdentify (config) {const url config.url// 返回url及请求参数 post方法请求参数为config.data get方法请求参数为config.paramsif (config.method post) {return encodeURIComponent(config.url JSON.stringify(config.data))}return encodeURIComponent(url JSON.stringify(config.params)) } 10、配置环境变量在项目根目录下创建 .env.development 文件内容如下 NODE_ENVdevelopment VUE_APP_TITLE development #测试环境,使用此配置文件 #请求前缀 VUE_APP_BASE_API http://localhost:8848/api 11、修改vue.config.js文件覆盖文件内容配置服务器端的IP和访问端口等文件的完整内容如下 module.exports {runtimeCompiler: true,lintOnSave: process.env.NODE_ENV ! production,lintOnSave: false,productionSourceMap:false,chainWebpack(config) {config.plugins.delete(prefetch) },pwa: {iconPaths: {}},devServer: {host: 0.0.0.0,port: 8089,proxy: {/api: {target: process.env.VUE_APP_BASE_API,pathRewrite:{^/api:/},changeOrigin: true,ws: false},}} } 13、运行测试前端Vue程序 进入命令行模式在项目根目录下执行npm run dev命令运行项目注意npm默认使用国外镜像可能会出现连接失败的问题如出现连接失败可将npm命令切换成cnpm命令 [rootclient KongGuan-Web]# npm run dev 打开浏览器可以查看到登录页面 二、登录功能的后端处理过程 1、在后端BigData-KongGuan项目的pom.xml文件中引入 spring-boot-starter-security 包和Redis相关包 当前项目使用SpringBoot的WebSecurityConfig安全组件并使用Redis保存用户token和权限并为Redis设置过期时间在前面搭建后端基础框架的任务中已经在pom.xml文件引入了相关依赖包打开pom.xml文件可以查看到以下内容。 !– Spring Boot Security –dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependency!– spring-redis –dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdexclusionsexclusiongroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactId/exclusion/exclusions/dependencydependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId/dependency 2、编写com/qrsoft/config/WebSecurityConfig.java类使用SpringBoot的WebSecurity安全组件 使用自定义身份验证组件TokenAuthenticationProvider // … …// … 略 …Autowiredprivate TokenAuthenticationProvider tokenAuthenticationProvider;Overrideprotected void configure(AuthenticationManagerBuilder auth) {// 使用自定义身份验证组件auth.authenticationProvider(tokenAuthenticationProvider);}// … 略 …// … … 在类中添加拦截器拦截请求如果非/api/login请求则执行TokenLoginFilter过滤器进行登录验证执行TokenAuthenticationFilter进行身份验证。 Override protected void configure(HttpSecurity http) throws Exception {/// … …// … 略 …// 添加拦截器http.addFilterBefore(new TokenLoginFilter(/api/login, authenticationManager(), redisTemplate), UsernamePasswordAuthenticationFilter.class).addFilterBefore(new TokenAuthenticationFilter(userMapper, redisTemplate), UsernamePasswordAuthenticationFilter.class);// … 略 …// … … } com/qrsoft/config/WebSecurityConfig.java类的完整代码如下 package com.qrsoft.config;import com.google.gson.Gson; import com.qrsoft.common.WrappedResult; import com.qrsoft.filter.TokenAuthenticationFilter; import com.qrsoft.filter.TokenAuthenticationProvider; import com.qrsoft.filter.TokenLoginFilter; import com.qrsoft.mapper.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.http.HttpServletResponse;Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate SysUserMapper userMapper;Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate TokenAuthenticationProvider tokenAuthenticationProvider;Overrideprotected void configure(AuthenticationManagerBuilder auth) {// 使用自定义身份验证组件auth.authenticationProvider(tokenAuthenticationProvider);}Overrideprotected void configure(HttpSecurity http) throws Exception {// 配置 CSRF 关闭,允许跨域访问http.csrf().disable();// 开启Spring Security cors支持,允许跨域访问http.cors();// 关闭 Sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 允许 登录接口 的无授权访问其他需要授权访问http.authorizeRequests().antMatchers(/).permitAll().anyRequest().authenticated();// 禁用缓存http.headers().cacheControl();// 添加拦截器http.addFilterBefore(new TokenLoginFilter(/api/login, authenticationManager(), redisTemplate), UsernamePasswordAuthenticationFilter.class).addFilterBefore(new TokenAuthenticationFilter(userMapper, redisTemplate), UsernamePasswordAuthenticationFilter.class);// 指定错误未授权访问的处理类http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) - {response.setContentType(application/json);response.setCharacterEncoding(UTF-8);response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.getWriter().print(new Gson().toJson(WrappedResult.failedWrappedResult(accessDeniedException.getMessage(), 403)));});}Overridepublic void configure(WebSecurity web) {web.ignoring().antMatchers(/doc.html/, /swagger-ui.html/, /v2/, /swagger-resources/, /webjars/, /minio/);} }package com.qrsoft.config;import com.google.gson.Gson; import com.qrsoft.common.WrappedResult; import com.qrsoft.filter.TokenAuthenticationFilter; import com.qrsoft.filter.TokenAuthenticationProvider; import com.qrsoft.filter.TokenLoginFilter; import com.qrsoft.mapper.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.http.HttpServletResponse;Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate SysUserMapper userMapper;Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate TokenAuthenticationProvider tokenAuthenticationProvider;Overrideprotected void configure(AuthenticationManagerBuilder auth) {// 使用自定义身份验证组件auth.authenticationProvider(tokenAuthenticationProvider);}Overrideprotected void configure(HttpSecurity http) throws Exception {// 配置 CSRF 关闭,允许跨域访问http.csrf().disable();// 开启Spring Security cors支持,允许跨域访问http.cors();// 关闭 Sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);// 允许 登录接口 的无授权访问其他需要授权访问http.authorizeRequests().antMatchers(/).permitAll().anyRequest().authenticated();// 禁用缓存http.headers().cacheControl();// 添加拦截器http.addFilterBefore(new TokenLoginFilter(/api/login, authenticationManager(), redisTemplate), UsernamePasswordAuthenticationFilter.class).addFilterBefore(new TokenAuthenticationFilter(userMapper, redisTemplate), UsernamePasswordAuthenticationFilter.class);// 指定错误未授权访问的处理类http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) - {response.setContentType(application/json);response.setCharacterEncoding(UTF-8);response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.getWriter().print(new Gson().toJson(WrappedResult.failedWrappedResult(accessDeniedException.getMessage(), 403)));});}Overridepublic void configure(WebSecurity web) {web.ignoring().antMatchers(/doc.html/, /swagger-ui.html/, /v2/, /swagger-resources/, /webjars/, /minio/);} } 3、编写com/qrsoft/filter下面的类TokenLoginFilter是登录过滤器除此之外还会用到TokenAuthenticationFilter身份认证过滤器和TokenAuthenticationProvider用来处理认证实体。 登录过程包括4个部分按①②③④的顺序执行其中com/qrsoft/filter/TokenLoginFilter.java类是登录的核心过滤器包括①④两个部分 package com.qrsoft.filter;import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.google.gson.Gson; import com.qrsoft.common.TokenVO; import com.qrsoft.common.WrappedResult; import com.qrsoft.entity.SysUser; import com.qrsoft.util.TokenUtil; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors;public class TokenLoginFilter extends AbstractAuthenticationProcessingFilter {private final StringRedisTemplate redisTemplate;public TokenLoginFilter(String url, AuthenticationManager authManager, StringRedisTemplate redisTemplate) {super(new AntPathRequestMatcher(url));setAuthenticationManager(authManager);this.redisTemplate redisTemplate;}/** 尝试认证从request中取用户名和密码生成认证实体/Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {return null;//① …}/** 认证成功返回统一格式的JSON/Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChainchain, Authentication auth) throws IOException {//④…}/** 认证失败返回统一格式的JSON/Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponseresponse, AuthenticationException failed) throws IOException {//④ …} } com/qrsoft/filter/TokenAuthenticationProvider.java类是用来处理认证实体包括②③两个部分 package com.qrsoft.filter;import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.qrsoft.entity.SysAuth; import com.qrsoft.entity.SysUser; import com.qrsoft.mapper.SysAuthMapper; import com.qrsoft.mapper.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component;import java.util.List; import java.util.Set; import java.util.stream.Collectors;Component public class TokenAuthenticationProvider implements AuthenticationProvider {private final PasswordEncoder passwordEncoder new BCryptPasswordEncoder();Autowiredprivate SysUserMapper userMapper;Autowiredprivate SysAuthMapper authMapper;/** 身份认证/Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {return null;//③ … }Overridepublic boolean supports(Class? authentication) {return false;//② … }/** 获取用户权限/private ListGrantedAuthority getGrantedAuthorities(SysUser sysUser) {ListSysAuth authList authMapper.getAuthByUserId(sysUser.getId());//先过滤在转换SetString perms authList.stream().map(SysAuth::getAuthCode).filter(StringUtils::isNotBlank).collect(Collectors.toSet());return AuthorityUtils.createAuthorityList(perms.toArray(new String[0]));} } 在TokenLoginFilter类中的 ① 部分的代码如下请在TokenLoginFilter类中的相应位置替换为如下代码 /** 尝试认证从request中取用户名和密码生成认证实体/Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException { //①String account request.getParameter(account);String password request.getParameter(password);if (StringUtils.isBlank(account) StringUtils.isBlank(password)) {InputStreamReader streamReader new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8);BufferedReader reader new BufferedReader(streamReader);StringBuilder builder new StringBuilder();String inputStr;while ((inputStr reader.readLine()) ! null)builder.append(inputStr);JSONObject jsonObject JSONObject.parseObject(builder.toString());account jsonObject.getString(account);password jsonObject.getString(password);streamReader.close();reader.close();}if (account.contains( )) throw new BadCredentialsException(用户名或密码错误);// 返回一个验证令牌return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(account, password));} 在TokenAuthenticationProvider类中的 ② 部分的代码如下请在TokenAuthenticationProvider类中的相应位置替换为如下代码 Overridepublic boolean supports(Class? authentication) {//②return authentication.equals(UsernamePasswordAuthenticationToken.class);} 在TokenAuthenticationProvider类中的 ③ 部分代码如下请在TokenAuthenticationProvider类中的相应位置替换为如下代码 /** 身份认证*/Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {//③if (authentication.getPrincipal() null || authentication.getCredentials() null)throw new BadCredentialsException(用户名或密码错误);// 获取认证的用户名 密码String account authentication.getPrincipal().toString();String password authentication.getCredentials().toString();SysUser sysUser userMapper.getByAccount(account);if (sysUser ! null) {// 密码不匹配直接抛出异常if (!passwordEncoder.matches(password, sysUser.getPassword()))throw new BadCredentialsException(用户名或密码错误);// 获取用户权限ListGrantedAuthority authorities getGrantedAuthorities(sysUser);UsernamePasswordAuthenticationToken token new UsernamePasswordAuthenticationToken(sysUser.getAccount(), sysUser.getPassword(), authorities);token.setDetails(sysUser);return token;} else {throw new UsernameNotFoundException(用户不存在或已删除);}} 在TokenLoginFilter类中的 ④ 部分的代码如下请在TokenLoginFilter类中的相应位置替换为如下代码 /**

  • 认证成功返回统一格式的JSON */ Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException {//④response.setContentType(application/json);response.setStatus(HttpServletResponse.SC_OK);response.setCharacterEncoding(UTF-8);String token;//生成tokentry {MapString, Object payload new HashMap();payload.put(account, auth.getName());token TokenUtil.genToken(payload);} catch (Exception e) {response.getWriter().print(new Gson().toJson(Token生成失败));return;}if (auth.getDetails() instanceof SysUser) {SysUser sysUser (SysUser) auth.getDetails();TokenVO tokenVO new TokenVO();tokenVO.setAccount(auth.getName());Collection? extends GrantedAuthority authorities auth.getAuthorities();ListString authorityList authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());tokenVO.setName(sysUser.getName());tokenVO.setId(sysUser.getId().toString());tokenVO.setType(sysUser.getType());Boolean userboo redisTemplate.hasKey(sysUser.getId().toString());if (userboo ! null userboo) {String tok redisTemplate.boundValueOps(sysUser.getId().toString()).get();if (tok ! null) {Boolean tokboo redisTemplate.hasKey(tok);if (tokboo ! null tokboo) {redisTemplate.delete(tok);}}redisTemplate.delete(sysUser.getId().toString());}//存储用户对应tokenredisTemplate.boundValueOps(sysUser.getId().toString()).set(token);//存储token对应权限BoundSetOperationsString, String setOperations redisTemplate.boundSetOps(token);setOperations.add(authorityList.toArray(new String[]{}));response.setHeader(Authorization, token);response.setHeader(userAuth,authorityList.toString());tokenVO.setAuthorization(token);tokenVO.setUserAuth(authorityList.toString());response.getWriter().print(new Gson().toJson(WrappedResult.successWrapedResult(tokenVO)));} else {response.setContentType(application/json);response.setStatus(HttpServletResponse.SC_OK);response.setCharacterEncoding(UTF-8);response.getWriter().print(new Gson().toJson(WrappedResult.failedWrappedResult(登录失败)));} } /**
  • 认证失败返回统一格式的JSON / Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {//④response.setContentType(application/json);response.setStatus(HttpServletResponse.SC_OK);response.setCharacterEncoding(UTF-8);response.getWriter().print(new Gson().toJson(WrappedResult.failedWrappedResult(failed.getMessage()))); } com/qrsoft/filter/TokenAuthenticationFilter.java类是用于身份认证的过滤器其完整代码如下 package com.qrsoft.filter;import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.google.gson.Gson; import com.qrsoft.common.R; import com.qrsoft.common.WrappedResult; import com.qrsoft.entity.SysUser; import com.qrsoft.mapper.SysUserMapper; import com.qrsoft.util.TokenUtil; import org.apache.commons.collections.MapUtils; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.GenericFilterBean;import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set;/** 身份认证过滤器/ public class TokenAuthenticationFilter extends GenericFilterBean {private final SysUserMapper userMapper;private final StringRedisTemplate redisTemplate;public TokenAuthenticationFilter(SysUserMapper sysUserDao, StringRedisTemplate redisTemplate) {this.userMapper sysUserDao;this.redisTemplate redisTemplate;}Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {System.out.println(权限信息打印);// 从Http头中取tokenString token ((HttpServletRequest) request).getHeader(Authorization);if (token ! null) {// 校验token并获取token中存储的用户名R r TokenUtil.valid(token);// 验证redis是否存在tokenBoolean tokenBoo redisTemplate.hasKey(token);if (tokenBoo ! null tokenBoo r.isSuccess()) {// 从token检验结果获取用户名String account MapUtils.getString(r.getPayloadMap(), account, StringUtils.EMPTY);// 根据token获取redis中权限setSetString authoritySet redisTemplate.boundSetOps(token).members();SysUser sysUser userMapper.getByAccount(account);UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(sysUser.getAccount(), sysUser.getPassword(), AuthorityUtils.createAuthorityList(authoritySet.toArray(new String[]{})));authentication.setDetails(sysUser);SecurityContextHolder.getContext().setAuthentication(authentication);filterChain.doFilter(request, response);} else {//token验证不通过writeResponse((HttpServletResponse) response, Token失效);}} else {// token不存在放回401未登录writeResponse((HttpServletResponse) response, 未登录);}}private void writeResponse(HttpServletResponse response, String str) throws IOException {response.setContentType(application/json);response.setCharacterEncoding(UTF-8);response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().print(new Gson().toJson(WrappedResult.failedWrappedResult(str, String.valueOf(HttpServletResponse.SC_UNAUTHORIZED))));} } 4、由于com.qrsoft.entity、com.qrsoft.mapper、com.qrsoft.common、com.qrsoft.util包下的帮助类不是当前项目的重点所以请参考相应的源代码自行完成或直接拷贝这些通用类即可。主要使用到以下的类 类/接口 功能 com.qrsoft.mapper.SysUserMapper用户基础信息表对应的数据访问类com.qrsoft.mapper.SysAuthMapper权限表对应的数据访问类com.qrsoft.common.R通用类定义了返回结果com.qrsoft.common.WrappedResult通用类定义了返回结果的接口规范com.qrsoft.common.TokenVO通用类定义了Token的内容com.qrsoft.common.AuthAndMenu通用类定义了权限和菜单com.qrsoft.entity.SysUser用户基础信息表对应的数据实体类com.qrsoft.entity.SysAuth权限表对应的数据实体类com.qrsoft.util.TokenUtilToken工具类用于生成Token、验证Token有效性等com.qrsoft.config.MybatisPlusConfigMybatisPlus的配置类 com.qrsoft.mapper.SysUserMapper类用户基础信息表对应的数据访问类 package com.qrsoft.mapper;import com.qrsoft.entity.SysUser; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository;Repository Mapper public interface SysUserMapper {Select(select id,account,password,name,is_enable,type,user_type_id from sys_user where account #{account} and is_del 0)SysUser getByAccount(String account); } com.qrsoft.mapper.SysAuthMapper类权限表对应的数据访问类 package com.qrsoft.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.qrsoft.entity.SysAuth; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import org.springframework.stereotype.Repository;import java.util.List;Repository Mapper public interface SysAuthMapper extends BaseMapperSysAuth {/** 根据用户id获取按钮权限/Select(select sa.id,sa.auth_name,sa.auth_code,sa.parent_id from sys_auth sa left join role_auth ra on sa.id ra.auth_id where sa.is_del 0 and sa.type 0 and ra.role_id in (select sr.id from sys_role sr join user_role ur on sr.id ur.role_id where sr.is_del 0 and sr.is_enable 0 and ur.user_id #{userId}) order by sa.parent_id)ListSysAuth getAuthByUserId(Integer userId);/** 根据用户id获取菜单权限/Select(select sa.id,sa.auth_name,sa.auth_code,sa.parent_id,sa.menu_url,sa.menu_icon,sa.menu_order from sys_auth sa left join role_auth ra on sa.id ra.auth_id where sa.is_del 0 and sa.type 1 and ra.role_id in (select sr.id from sys_role sr join user_role ur on sr.id ur.role_id where sr.is_del 0 and sr.is_enable 0 and ur.user_id #{userId}) group by sa.id order by sa.menu_order)ListSysAuth getMenuByUserId(Integer userId);/** 根据权限id获取权限详情/Select(select id,auth_name,auth_code,type,menu_url,parent_id,menu_icon,menu_order from sys_auth where id #{id} and is_del0)SysAuth getOneAuth(Integer id);/** 根据权限标识统计权限数量/Select(select count(1) from sys_auth where auth_code #{authCode} and is_del0)Integer countAuthCode(String authCode);/** 权限取消全部角色/Delete(delete from role_auth where auth_id #{authId})void deleteRole(Integer authId);/** 删除权限/Update(update sys_auth set is_del 1 where id #{id} and is_del 0)Boolean delAuth(Integer id);Select(select DISTINCT sa.id,sa.auth_name,sa.auth_code,sa.parent_id,sa.type,sa.menu_icon,sa.menu_order from sys_auth sa left join role_auth ra on sa.id ra.auth_id where sa.is_del 0 and ra.role_id #{roleId} order by sa.parent_id)ListSysAuth getAuthByRole(Integer roleId);/** 根据角色获取权限id/Select(select auth_id from role_auth where role_id in (${roleId}))ListInteger getIdByRole(String roleId); } com.qrsoft.common.R类通用类定义了返回结果 package com.qrsoft.common;import java.util.HashMap; import java.util.Map;public class R {private final boolean success;private final String msg;private final MapString, Object payloadMap;private R(boolean success, String msg, MapString, Object payloadMap) {this.success success;this.msg msg;this.payloadMap payloadMap;}public static R ok(MapString, Object payloadMap) {return new R(true, , payloadMap);}public static R error(String msg) {return new R(false, msg, new HashMap());}public boolean isSuccess() {return success;}public MapString, Object getPayloadMap() {return payloadMap;}Overridepublic String toString() {return R{ success success , msg msg \ , payloadMap payloadMap };} } com.qrsoft.common.WrappedResult类通用类定义了返回结果的接口规范 package com.qrsoft.common;import io.swagger.annotations.ApiModelProperty; import org.springframework.security.access.AccessDeniedException; import java.io.Serializable;public class WrappedResultT implements Serializable {private static final long serialVersionUID 1L;private static final String ERROR error;private static final String SUCCESS success;/** 接口状态/ApiModelProperty(接口状态true成功false失败)private final boolean successful;/** 接口返回数据/ApiModelProperty(接口成功返回数据主体)private final T resultValue;/** 接口错误信息/ApiModelProperty(接口错误信息)private final String resultHint;/** 接口返回状态* 失败401403error* 成功success/ApiModelProperty(接口返回状态成功success失败401403error)private final String type;private WrappedResult(boolean isSuccess, T data, String resultHint, String type) {this.successful isSuccess;this.resultValue data;this.resultHint resultHint;this.type type;}public static T WrappedResultT successWrapedResult(T data) {return new WrappedResult(true, data, , SUCCESS);}public static T WrappedResultT failedWrappedResult(String exMessage) {return new WrappedResult(false, null, exMessage, ERROR);}public static WrappedResultBoolean failedWrappedResult(Exception e, Boolean data) {if (java.lang.RuntimeException.equals(e.getClass().getName())) {return new WrappedResult(false, data, e.getMessage(), ERROR);} else if (org.springframework.security.access.AccessDeniedException.equals(e.getClass().getName())) {throw new AccessDeniedException(e.getMessage());} else {return new WrappedResult(false, data, 操作失败, ERROR);}}public static T WrappedResultT failedWrappedResult(Exception e) {if (java.lang.RuntimeException.equals(e.getClass().getName())) {return new WrappedResult(false, null, e.getMessage(), ERROR);} else if (org.springframework.security.access.AccessDeniedException.equals(e.getClass().getName())) {throw new AccessDeniedException(e.getMessage());} else {return new WrappedResult(false, null, 操作失败, ERROR);}}public static T WrappedResultT failedWrappedResult(Exception e, String exMessage) {if (java.lang.RuntimeException.equals(e.getClass().getName())) {return new WrappedResult(false, null, e.getMessage(), ERROR);} else if (org.springframework.security.access.AccessDeniedException.equals(e.getClass().getName())) {throw new AccessDeniedException(e.getMessage());} else {return new WrappedResult(false, null, exMessage, ERROR);}}public static T WrappedResultT failedWrappedResult(String exMessage, String type) {return new WrappedResult(false, null, exMessage, type);}public boolean isSuccessful() {return this.successful;}public T getResultValue() {return this.resultValue;}public String getType() {return this.type;}public String getResultHint() {return resultHint;} } com.qrsoft.common.TokenVO类通用类定义了Token的内容 package com.qrsoft.common;import lombok.Data;import java.io.Serializable; import java.util.List;Data public class TokenVO implements Serializable {private static final long serialVersionUID -5501706435587205188L;/** token/private String token;/** 用户ID/private String id;/** 姓名/private String name;/** 用户名/private String account;/** 用户类型/private Integer type;/** 按钮权限列表/private ListAuthAndMenu authList;/** 菜单权限列表/private ListAuthAndMenu menuList;private String Authorization;private String userAuth; } com.qrsoft.common.AuthAndMenu类 通用类定义了权限和菜单 package com.qrsoft.common;import com.qrsoft.entity.SysAuth; import lombok.Data;import java.util.List;Data public class AuthAndMenu {/** id/private Integer id;/** 权限名称/private String authName;/** 权限编码/private String authCode;/** 权限类型0按钮1菜单/private Integer type;/** 菜单Url/private String menuUrl;/** 菜单Icon/private String menuIcon;/** 菜单order/private Integer menuOrder;/** 子菜单/private ListAuthAndMenu childs;/** 父级id/private Integer parentId;public static AuthAndMenu authTOAuth(SysAuth auth) {AuthAndMenu output new AuthAndMenu();output.setId(auth.getId());output.setAuthName(auth.getAuthName());output.setAuthCode(auth.getAuthCode());output.setType(0);output.setParentId(auth.getParentId());output.setMenuUrl(auth.getMenuUrl());output.setMenuIcon(auth.getMenuIcon());output.setMenuOrder(auth.getMenuOrder());return output;}public static AuthAndMenu authTOMenu(SysAuth auth) {AuthAndMenu output new AuthAndMenu();output.setId(auth.getId());output.setAuthName(auth.getAuthName());output.setType(1);output.setParentId(auth.getParentId());output.setMenuUrl(auth.getMenuUrl());output.setMenuIcon(auth.getMenuIcon());output.setMenuOrder(auth.getMenuOrder());return output;} } com.qrsoft.entity.SysUser类用户基础信息表对应的数据实体类 package com.qrsoft.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;import java.io.Serializable; import java.util.Date;/** 用户基础/ Data TableName(sys_user) public class SysUser implements Serializable {private static final long serialVersionUID 1L;/** 主键id/TableId(value id, type IdType.AUTO)private Integer id;/** 用户账号/TableField(account)private String account;/** 用户密码/TableField(password)private String password;/** 用户姓名/TableField(name)private String name;/** 联系方式/TableField(contact)private String contact;/** 用户类型0质检员(管理员)1司机2客户/TableField(type)private Integer type 0;/** 司机id/客户id/TableField(user_type_id)private Integer userTypeId;/** 是否启用启用0未启用1/TableField(is_enable)private Integer isEnable 0;/** 是否删除正常0删除1/TableField(is_del)private Integer isDel 0;/** 创建人/TableField(value create_user, select false)private Integer createUser;/** 创建时间/TableField(value create_time, select false)private Date createTime; } com.qrsoft.entity.SysAuth类权限表对应的数据实体类 package com.qrsoft.entity;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;import java.io.Serializable; import java.util.Date;/** 权限表/ Data TableName(sys_auth) public class SysAuth implements Serializable {private static final long serialVersionUID 1L;/** 主键id/TableId(value id, type IdType.AUTO)private Integer id;/** 权限名称/TableField(auth_name)private String authName;/** 权限编码/TableField(auth_code)private String authCode;/** 权限类型0按钮1菜单/TableField(type)private Integer type 0;/** 菜单Url/TableField(menu_url)private String menuUrl;/** 父级id/TableField(parent_id)private Integer parentId;/** 菜单图标/TableField(menu_icon)private String menuIcon;/** 菜单顺序/TableField(menu_order)private Integer menuOrder;/** 删除状态0正常1删除/TableField(is_del)private Integer isDel 0;/** 创建人/TableField(value create_user, select false)private Integer createUser;/** 创建时间/TableField(value create_time, select false)private Date createTime; } com.qrsoft.util.TokenUtil类Token工具类用于生成Token、验证Token有效性等 package com.qrsoft.util;import com.qrsoft.common.R; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys;import java.util.Calendar; import java.util.Map;/** token工具类/ public class TokenUtil {private TokenUtil() {}/** 密钥至少32字节后续可根据需求换成RSA加密/private static final byte[] SECRET sarnath-sarnath-sarnath-sarnath..getBytes();/** token失效时间(分)*/public static final int EXP_TIME 24 * 60;/*** 生成token/public static String genToken(MapString, Object payloadMap) {// JwtBuilder的base64UrlEncoder默认Encoders.BASE64URLJwtBuilder jwtBuilder Jwts.builder();// 设置载荷jwtBuilder.setClaims(payloadMap);// 设置失效时间Calendar calendar Calendar.getInstance();calendar.add(Calendar.MINUTE, EXP_TIME);jwtBuilder.setExpiration(calendar.getTime());// 设置签名jwtBuilder.signWith(Keys.hmacShaKeyFor(SECRET));return jwtBuilder.compact();}/** 验证token有效性*/public static R valid(String token) {try {JwtParser jwtParser Jwts.parser();// 设置时钟偏移jwtParser.setAllowedClockSkewSeconds(3 * 60);// 设置签名jwtParser.setSigningKey(SECRET);MapString, Object payload jwtParser.parseClaimsJws(token).getBody();return R.ok(payload);} catch (Exception e) {return R.error(e.getMessage());}} } com.qrsoft.config.MyBatisPlusConfig类MyBatisPlus的配置类 package com.qrsoft.config;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class MyBatisPlusConfig {Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor new PaginationInterceptor();paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));return paginationInterceptor;} } 5、因为需要访问MySQL数据库所以在resources目录下打开application.yml文件在前面步骤中已经创建并填写如下配置 server:port: 8848 spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: root password: 123456 url: jdbc:mysql://node3:3306/kongguan?autoReconnecttrueautoReconnectForPoolstruefailOverReadOnlyfalseserverTimezoneUTC redis:host: node3 port: 6379 database: 15 注意需要确保node3节点上的Redis和MySQL都已经正常启动可以参照前面安装部署的任务中的步骤进行验证。 6、项目启动类BigDataKongGuanApplication 的内容如下 package com.qrsoft;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableScheduling;SpringBootApplication EnableScheduling public class BigDataKongGuanApplication {public static ConfigurableApplicationContext appConfig;public static void main(String[] args) {appConfigSpringApplication.run(BigDataKongGuanApplication.class, args);} } 三、测试登录功能 1、启动后端Spring Boot程序。 2、启动前端VUE程序。 3、输入用户名和密码例如admin/admin进行登录。