大埔县住房和城乡规划建设局网站公司企业做网站好做吗

web/2025/10/5 15:52:37/文章来源:
大埔县住房和城乡规划建设局网站,公司企业做网站好做吗,如何做好网站建设内容的策划,wordpress评论邮件前言#xff1a; 目前#xff0c;前后端分离开发已经成为当前web开发的主流。目前最流行的技术选型是前端vue3后端的spring boot3#xff0c;本次。就基于这两个市面上主流的框架来开发出一套基本的后台管理系统的模板#xff0c;以便于我们今后的开发。 前端使用vue3ele…前言 目前前后端分离开发已经成为当前web开发的主流。目前最流行的技术选型是前端vue3后端的spring boot3本次。就基于这两个市面上主流的框架来开发出一套基本的后台管理系统的模板以便于我们今后的开发。 前端使用vue3element-plus开发。后端使用spring boot3spring security作为项目的支撑使用MySQL8.0.30作数据存储使用redis作缓存使用minio作为项目的存储机构。 后台管理系统是比较注重权限的本项目使用市面上最流行的RBAC模型。建立用户、角色、权限和它们两两之间的对映关系一共五张表日志管理两张表一张记录用户的行为、一张记录用户的操作。 搭建基础的环境 后端 创建一个spring boot项目并导入一些基础的maven依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdcom.github.xiaoymin/groupIdartifactIdknife4j-openapi3-jakarta-spring-boot-starter/artifactIdversion4.3.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-mail/artifactId/dependencydependencygroupIdcom.alibaba.fastjson2/groupIdartifactIdfastjson2/artifactIdversion2.0.21/version/dependencydependencygroupIdio.minio/groupIdartifactIdminio/artifactIdversion8.5.2/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.4/version/dependencydependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.22/version/dependencydependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.3.0/version/dependencydependencygroupIdcom.github.pagehelper/groupIdartifactIdpagehelper-spring-boot-starter/artifactIdversion1.4.7/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid-spring-boot-starter/artifactIdversion1.2.6/version/dependencydependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies项目使用MybatisPlus进行数据库的操作使用redis作为缓存。使用minio作为项目的存储机构。 MySQL是我本地的服务redis和minio服务我放在了Linux服务器上。如果有对minio不熟悉的可以看一下我之前写过的文章 springboot整合minio实现文件的上传和下载超详细入门_minio下载文件-CSDN博客 根据数据库中的表先创建出相应的controller、service、mapper和相应的实体类。我直接使用mybatisX插件进行相应数据的生成。一共有七张表相应的SQL脚本我会连同前后端的代码一起放在git。 至此。后端项目就暂时搭建出了一个基础的模板我们接下来开始进行前端项目的部署 前端 挑选一个文件夹运行 npm create vuelatest 命令来创建一个基础的前端vue3项目在创建项目时可以进行一些基础配置的选择。我在创建前端项目时选择的编程语言是js如果有选择ts的可能需要对数据的类型进行相应的指定。 在创建完前端项目之后我们可能还需要引入一些相应的包。 npm install element-plus --save npm install element-plus/icons-vue npm install sass npm install pinia-plugin-persistedstate npm install axios 在项目的终端运行命令来完成相应的依赖下载。下载完成之后。在package.json文件中相应的依赖如下 至此我们的前端vue3项目也已经搭建完成了接下来就可以开始我们前后端代码的编写了。 代码编写 先进行前端代码的编写一步步向后端靠拢最终完成我们要实现的功能。 前端代码 在搭建环境时我们引入了一些包的依赖我们需要在main.js中进行依赖的声明和引用。 import { createApp } from vue import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import zhCn from element-plus/es/locale/lang/zh-cn import App from /App.vue import router from /router // 1、pinia的持久化插件 import { createPersistedState } from pinia-plugin-persistedstate // element-plus的图标 import * as ElementPlusIconsVue from element-plus/icons-vueconst app createApp(App) const pinia createPinia()//2、 接收createPersistedState函数 const piniaPersistedState createPersistedState()// 3、在pinia中引入持久化插件 pinia.use(piniaPersistedState)// 全局引入图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)}app.use(pinia) app.use(router) app.use(ElementPlus, {locale: zhCn, }) app.mount(#app)删除vue3项目自带的一些vue组件将整个vue项目恢复成一个纯净的项目。 首先我们要做的页面是后台管理的登录页面。 在views目录下创建一个Login.vue 页面这个页面中进行我们后台管理系统的登录操作。 在router路由中进行我们登录页面的配置要求在运行项目时首先跳转的就是登录页面这也符合我们项目的预期后台管理类的所有项目一定是要先登录接下来才能进行功能的操作 import { createRouter, createWebHistory } from vue-router import useToeknStore from /stores/useTokenconst router createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [// 系统用户登录{path: /,name: login,component: () import(/views/Login.vue)}]})// 前置守卫 // 全局拦截、除了登录页面其他页面都必须授权这里为pinia定义的token不为空才能访问 router.beforeEach((to, from) {const useTokenuseToeknStore()if (to.name ! login !useToken.token) {// alert(没有登录自动跳转到登录页面)return { name: login }}else{return true}})export default router在这个路由文件中不仅定义了登录页面同时引入了一个路由前置守卫。这个守卫的功能是如果没有登录那么就只能访问登录页面。 判断有没有登录的标识就是pinia中token有没有值。如果登录成功那么就会在pipin中部存入token的值如果退出登录。那么前端也会删除token的值。借此我们就可以判断出用户有没有登录了。我们知道存入pipin中的值其实是存储到了我们浏览器的localstore中对于稍微懂点前端的人来说都是很容易获取和改变的。在真实的项目中肯定是不会使用这么简单的方法的。这个项目相当于我们个人开发的一个简单项目所以就无所谓了。 接下来我们对axios进行一下封装这样每次发送请求到后端时就可以大大简化了。 创建util目录在这个目录下新建一个request.js文件在这个文件中封装我们axios。 import axios from axios; import useTokenStore from /stores/useToken import { ElMessage } from element-plus; // 先建一个api系统 const api axios.create({baseURL: http://localhost:8888,timeout: 5000});// 发送请求前拦截 api.interceptors.request.use(config { const useToken useTokenStore(); // 系统用户的请求头 if(useToken.token){console.log(请求头toekn, useToken.token);// 设置请求头// config.headers[token] useToken.token;config.headers.token useToken.token; }return config;}, error {return Promise.reject(error); } )// 响应前拦截 api.interceptors.response.use(response {console.log(响应数据, response); if(response.data.code 200){ return response.data; }// 响应失败 if(response.data.code !200){ console.log(响应失败, response.data);}return response.data; }, error {return Promise.reject(error); } )export {api}现在我们就可以正式的编写我们后台管理系统的登录页面了。 (需要注意的是我们的系统是一个后台管理类的系统所以在首页不能让所有用户自行注册在首页就写一个登录按钮和一个忘记密码的按钮。用户的添加需要有权限的用户在后台管理系统的功能区进行添加才行) 登录页面的内容如下 template!-- stylefont-family:kaiti --div classbackground !-- 注册表单,一个对话框 -- el-dialog v-modelisRegister title用户注册 width30% draggabletrueel-form label-width120px v-modelregisterFormel-form-item label用户名el-input typetext v-modelregisterForm.username template #prefixel-iconAvatar //el-icon/template/el-input/el-form-itemel-form-item label密码el-input typepassword v-modelregisterForm.password template #prefixel-iconLock //el-icon/template/el-input/el-form-itemel-form-itemel-button typeprimary clickregisterAdd 提交/el-buttonel-button clickcloseRegister取消/el-button/el-form-item/el-form /el-dialog!-- 登陆框 -- div classlogin-box el-formlabel-width100px:modelloginFromstylemax-width: 460px:rulesLoginrulesrefruleFormRefdiv style text-align: center;font-weight: bold;后台管理系统模板/div el-form-item label用户名 propusernameel-input v-modelloginFrom.username clearable template #prefixel-iconAvatar //el-icon/template/el-input/el-form-itemel-form-item label密码 proppasswordel-input v-modelloginFrom.password show-password clearable typepassword template #prefixel-iconLock //el-icon/template/el-input/el-form-itemel-form-item label验证码 propcodeValueel-input v-modelloginFrom.codeValue stylewidth: 100px; clearable /el-inputimg :srccodeImage clickgetCode styletransform: scale(0.9);//el-form-item!-- el-checkbox v-modelrememberMe.rememberMe 记住我/el-checkbox --!-- 跳转一个新页面 --el-link typeprimary styletransform: translateX(330px) clickresetPassword()忘记密码/el-linkbr el-button typesuccess clickgetLogin(ruleFormRef) sizesmall classmy-button登录/el-button!-- el-button typeprimary clickisRegistertrue classmy-button注册/el-button --/el-form!-- 按下enter键提交登录请求 --!-- input keyup.entergetLogin(ruleFormRef) --/div/div /templatescript setup import { ref,onMounted,reactive, onUnmounted } from vue; import { useRouter } from vue-router; import { ElMessage } from element-plus; import useTokenStore from /stores/useToken import { getCodeApi ,loginApi,registerApi} from /api/SysLogin;// !-- 按下enter键提交登录请求 --const keyDownHandler (event) {if (event.key Enter) {// 执行你想要的操作console.log(Enter键被按下了);getLogin(ruleFormRef.value)}}onMounted(() {window.addEventListener(keydown, keyDownHandler);});onUnmounted(() {window.removeEventListener(keydown, keyDownHandler);});// 重置密码 const resetPassword(){router.push({name:resetPassword,query:{type:sys }})}const ruleFormRef ref()// const rememberMerememberMeStore()const loginFromref({ username:, password:, codeKey:, codeValue: // rememberMe:rememberMe.rememberMe })const Loginrulesreactive({username: [{ required: true, message: 请输入用户名, trigger: blur }],password: [{ required: true, message: 请输入密码, trigger: blur },{ min: 6, max: 12, message: 长度在 6 到 12 个字符, trigger: blur}],codeValue: [{ required: true, message: 请输入验证码, trigger: blur }]})const registerFormref({username:,password: })const codeImageref()const isRegisterref(false)const tokenStore useTokenStore();const router useRouter()// 登录,提交阶段 const getLogin async(formEl) {if (!formEl) returnawait formEl.validate(async (valid, fields) {if (valid) {console.log(submit!)let data await loginApi(loginFrom.value)console.log(几点几分上了飞机,data.code);if(data.code200){ElMessage(登录成功)console.log(data.data);tokenStore.tokendata.datarouter.replace({name:layout})}else{ElMessage(登录失败)ElMessage(失败原因data.message)}} else {ElMessage(请输入完整信息)return;}})}const getCodeasync(){let {data}await getCodeApi()console.log(data);console.log(验证码的值为,data);loginFrom.value.codeKeydata.codeKeycodeImage.valuedata.codeValue }// 注册,提交阶段 const registerAddasync(){let dataawait registerApi(registerForm.value)if(data.code200){ElMessage(注册成功)registerForm.value{username:,password:}isRegister.valuefalse }else{ElMessage(注册失败)registerForm.value{username:,password:}isRegister.valuefalse}}const closeRegister(){registerForm.value{username:,password:}isRegister.valuefalse }// 页面加载完成获取验证码onMounted((){getCode()})/scriptstyle langscss scoped // 登录页面总样式 .background{background: url(/assets/20.png);width:100%;height:100%; /**宽高100%是为了图片铺满屏幕 */position: fixed;background-size: 100% 100%;font-family:kaiti }.login-box{border:1px solid #dccfcf;width: 350px;margin:180px auto;padding: 20px 50px 20px 50px;border-radius: 5px;-webkit-border-radius: 5px;-moz-border-radius: 5px;box-shadow: 0 0 25px #909399;background-color:rgba(255,255,255,0.7);//这里最后那个0.7就是为了防止背景表单颜色太浅 }.my-button {margin-right: 100px;width: 400px;padding: 10px 20px;font-size: 16px;background-color: #4CAF50;color: white;border: none;border-radius: 5px;cursor: pointer;}/style 具体的展现效果如图 挂载组件时会发送一个请求验证码的接口到后端。后端我会使用hutoll工具包生成验证码。并将验证码放在redis中并设置过期时间为5分钟。再通过base64编码的形式将验证码的图片传到前端直接显示。 在这个登录页面中用户需要输入用户名、密码和验证码到后端。后端会先验证验证码的值如果正确再验证用户名和密码。如果都正确那么后端会返回登录成功的提示并根据用户id生成一个token返回前端前端收到token之后会将这个token存入pinia中。接下来跳转到后台管理系统的功能页面 登录页面有一个“忘记密码”的按钮我们通过这个按钮应该能实现用户密码的重置。 密码重置通过用户邮箱发送验证码来进行验证也可以使用短信验证码的形式进行验证都是可以的 重置密码页面 templatediv classresetPassword h1密码重置页面/h1 请输入邮箱input v-modelemailNumber classinputEmail typeemail /input el-button typeprimary clicksendEmail重置密码/el-buttonbr 请输入验证码input typetext v-modelcode classinputEmail/el-button typesuccess clicksendCode提交/el-buttondiv !-- 修改密码表单,一个对话框 -- el-dialog v-modelisPassword title用户修改密码 width30% draggabletrueel-form label-width120px v-modelpasswordFromel-form-item label密码el-input typepassword v-modelpasswordFrom.password show-password clearable template #prefixel-iconLock //el-icon/template/el-input/el-form-itemel-form-item label确认密码el-input typepassword v-modelpasswordFrom.doPassword show-password clearable template #prefixel-iconLock //el-icon/template/el-input/el-form-itemel-form-itemel-button typeprimary clicksendPassword 提交/el-buttonel-button clickcloseRegister取消/el-button/el-form-item/el-form /el-dialog /div /div/templatescript setup import {resetPassword,sendSysCode,sendSysPassword} from /api/ResetPassword import { ref } from vue; import { useRouter } from vue-router;let emailNumber ref()let code ref()let isPassword ref(false)let passwordFrom ref({password:,doPassword:,email: })const router useRouter()const sendEmail async() { console.log(emailNumber.value);const data await resetPassword(emailNumber.value)console.log(data);}const sendCode async() {const data await sendSysCode(code.value,emailNumber.value)if(data.code200){alert(验证码正确请修改密码)isPassword.valuetrue }else{alert(验证码错误请重新输入) }console.log(data);}const sendPassword async() {if(passwordFrom.value.passwordpasswordFrom.value.doPassword){passwordFrom.value.emailemailNumber.valueconst data await sendSysPassword(passwordFrom.value)console.log(data);if(data.code200){alert(密码修改成功)isPassword.valuefalserouter.replace({name:login})}else{alert(密码修改失败data.message)isPassword.valuefalse}}else{alert(两次密码不一致,请重新输入)}}const closeRegister () {isPassword.valuefalse }/scriptstyle langscss scoped .inputEmail{width: 300px;height: 40px; }.resetPassword{max-width: 400px;margin: 0 auto;padding: 20px;background-color: #f0f2f5;border-radius: 8px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);text-align: center;}/style 在路由中添加相应的路径并在路由守卫中加入重置页面。因为这个页面是不需要登录就能访问的 import { createRouter, createWebHistory } from vue-router import useToeknStore from /stores/useTokenconst router createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [// 系统用户登录{path: /,name: login,component: () import(/views/Login.vue)},// 重置密码页面{path: /resetPassword,name: resetPassword,component: () import(/views/ResetPassword.vue)},// 通配符路由匹配所有无法识别的路径 {path: /:pathMatch(.*)*,component: () import(/error/NotFount.vue) }]})// 前置守卫 // 全局拦截、除了登录页面其他页面都必须授权这里为pinia定义的token不为空才能访问 router.beforeEach((to, from) {const useTokenuseToeknStore()if (to.name ! login to.name!resetPassword !useToken.token) {// alert(没有登录自动跳转到登录页面)return { name: login }}else{return true}})export default router再写一个错误路由统一处理的页面并加入到路由中。 接下来就可以编写后端的代码来实现登录功能了 后端代码 由于我们后端使用了spring security作为安全框架。所以在controller层编写登录逻辑之前我们还需要在后端做一些security的处理。 在yml配置文件中进行一些信息的配置 spring:data:redis:host: 192.168.231.110port: 6379password: 123456database: 0timeout: 1000datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/zhangqiao-admin?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneUTCusername: rootpassword: 123456mail:host: smtp.qq.comusername: 2996809820qq.compassword: jbtjqbhxeaerdfdidefault-encoding: UTF-8servlet:multipart:max-file-size: 10MB # 单个文件上传的最大上限max-request-size: 100MB # 整个请求体的最大上限 mybatis-plus:global-config:db-config:id-type: autologic-delete-value: 1logic-not-delete-value: 0logic-delete-field: isDeletedconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpllogging:file:path: D:/logs/zhangqiao-admin/spring-admin# PageHelper 分页插件配置 pagehelper:helper-dialect: mysqlreasonable: truesupport-methods-arguments: trueparams: countcountsql minio:url: http://192.168.231.110:9001username: adminpassword: admin123456bucketName: zhangqiao-adminexclude:syspaths:- /sys/getCaptcha- /sys/user/login- /sys/resetPassword- /sys/sendSysCode- /sys/sendSysPassword # - /sys/user/addUserjwt: # expiration: 3600000Lsecret: zhangqiao 创建一个Security的配置类。来编写spring security的一些配置。 Configuration EnableWebSecurity public class MyServiceConfig {Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;Resourceprivate ExcludeSysPath excludeSysPath;/** security的过滤器链* */Resourceprivate CustomAccessDeniedHandler customAccessDeniedHandler;Resourceprivate CustomAuthenticationEntryPoint customAuthenticationEntryPoint;Bean public SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception { http.csrf(AbstractHttpConfigurer::disable);http.authorizeHttpRequests((auth) -auth.requestMatchers(excludeSysPath.getSyspaths().toArray(new String[0])).permitAll().anyRequest().authenticated());http.exceptionHandling(e - e.accessDeniedHandler(customAccessDeniedHandler).authenticationEntryPoint(customAuthenticationEntryPoint));http.cors(cors-{cors.configurationSource(corsConfigurationSource());}); //自定义过滤器放在UsernamePasswordAuthenticationFilter过滤器之前http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);return http.build(); }Autowired private MyUserDetailServerImpl myUserDetailsService;/* * 验证管理器 * */Beanpublic AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){DaoAuthenticationProvider providernew DaoAuthenticationProvider(); //将编写的UserDetailsService注入进来provider.setUserDetailsService(myUserDetailsService); //将使用的密码编译器加入进来provider.setPasswordEncoder(passwordEncoder); //将provider放置到AuthenticationManager 中ProviderManager providerManagernew ProviderManager(provider);return providerManager;}//跨域配置Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList(*));configuration.setAllowedMethods(Arrays.asList(*));configuration.setAllowedHeaders(Arrays.asList(*));UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration(/**, configuration);return source;}/** 密码加密器*/ Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}} 这个配置类中编写了密码加密器、跨域相关、验证管理、定义了一个有关排除路径的bean注入进配置文件。配置了未登录和权限不足时后端返回的一些具体信息添加了一个JWT的拦截器放在了security的登录拦截器之前。我们知道security的实现就是基于拦截器链的形式在登录拦截器之前加入JWT的token拦截器这样就能实现我们已经登录过的用户再访问其他资源时能正常访问。并且在 JWT拦截器中实现token的刷新 定义一个MyTUserDetail实现UserDetails接口来当security的用户类 Data public class MyTUserDetail implements Serializable, UserDetails {private static final long serialVersionUID 1L;private User users;// 角色private ListString roles;// 权限private ListString permissions;JsonIgnore //json忽略Overridepublic Collection? extends GrantedAuthority getAuthorities() {ListGrantedAuthority list new ArrayList();// 如果角色不用空则将角色添加到list中if (!ObjectUtils.isEmpty(roles)){roles.forEach(role-list.add(new SimpleGrantedAuthority(role)));}// 如果权限不用空则将权限添加到list中if (!ObjectUtils.isEmpty(permissions)){permissions.forEach(permission-list.add(new SimpleGrantedAuthority(permission)));}return list;}JsonIgnoreOverridepublic String getPassword() {return this.getUsers().getPassword();}JsonIgnoreOverridepublic String getUsername() {return this.getUsers().getUsername();}JsonIgnoreOverridepublic boolean isAccountNonExpired() {return this.getUsers().getStatus()0;}JsonIgnoreOverridepublic boolean isAccountNonLocked() {return this.getUsers().getStatus()0;}JsonIgnoreOverridepublic boolean isCredentialsNonExpired() {return this.getUsers().getStatus()0;}JsonIgnoreOverridepublic boolean isEnabled() {return this.getUsers().getStatus()0;} } 定义一个MyUserDetailServerImpl类实现UserDetailsService接口在这个类中实现用户名和密码的具体查询现在我还没有实现权限的查询按理说在登录时就应该一块进行的现在我只进行用户的登录查询权限等到之后再开发。 Service Slf4j public class MyUserDetailServerImpl implements UserDetailsService {AutowiredUserMapper userService;Autowired private RedisTemplateString,String redisTemplate;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user userService.selectOne(new LambdaQueryWrapperUser().eq(StringUtils.hasText(username), User::getUsername, username));if (user null) {throw new UsernameNotFoundException(用户名不存在);}MyTUserDetail myTUserDetailnew MyTUserDetail();myTUserDetail.setUsers(user);return myTUserDetail;} } JWT的token拦截器获取登录的用户信息和用户拥有的权限放在SecurityContextHolder。在后面要用到用户信息时可以直接在SecurityContextHolder中得到登陆的用户信息。并同时在JWT拦截器中进行过期时间的刷新。 Component Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {Autowiredprivate RedisTemplateString,String redisTemplate;Autowiredprivate JwtUtil jwtUtil;Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取请求头中的tokenString token request.getHeader(token);System.out.println(前端的token信息token);//如果token为空直接放行由于用户信息没有存放在SecurityContextHolder.getContext()中所以后面的过滤器依旧认证失败符合要求if(!StringUtils.hasText(token)){filterChain.doFilter(request,response);return;}// 解析Jwt中的用户idInteger userId jwtUtil.getUsernameFromToken(token);//从redis中获取用户信息String redisUser redisTemplate.opsForValue().get(UserLogin: userId);if(!StringUtils.hasText(redisUser)){filterChain.doFilter(request,response);return;}// 刷新tokenString newToken jwtUtil.refreshToken(token);redisTemplate.opsForValue().set(UserLogin:userId,redisUser,60, TimeUnit.MINUTES);// 将redis中的用户信息转换成MyTUserDetail对象MyTUserDetail myTUserDetail JSON.parseObject(redisUser, MyTUserDetail.class);log.info(Jwt过滤器中MyUserDetail的值myTUserDetail.toString());//将用户信息存放在SecurityContextHolder.getContext()后面的过滤器就可以获得用户信息了。这表明当前这个用户是登录过的后续的拦截器就不用再拦截了UsernamePasswordAuthenticationToken usernamePasswordAuthenticationTokennew UsernamePasswordAuthenticationToken(myTUserDetail,null,myTUserDetail.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);filterChain.doFilter(request,response);}} 现在我们就可以在controller中实现用户的登录功能了。 用户登录controller   PostMapping(/login)public ResultString login(RequestBody LoginDto loginDto){String token userService.login(loginDto);return Result.successData(token);} 相应的service实现 Autowired private RedisTemplateString,String redisTemplate;AutowiredAuthenticationManager authenticationManager;Overridepublic String login(LoginDto loginDto) { // 先检验验证码String codeRedis redisTemplate.opsForValue().get(loginDto.getCodeKey());if (codeRedisnull){throw new ResultException(555,验证码不存在);}if (!codeRedis.equals(loginDto.getCodeValue().toLowerCase())) {throw new ResultException(555, 验证码错误);} // 验证码正确删除redis中的验证码redisTemplate.delete(loginDto.getCodeKey());log.info(用户登录); // 用户登录UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(loginDto.getUsername(),loginDto.getPassword());Authentication authenticate authenticationManager.authenticate(authenticationToken);if(authenticatenull){throw new ResultException(400,用户名或密码错误);} // 获取返回的用户信息Object principal authenticate.getPrincipal();MyTUserDetail myTUserDetail(MyTUserDetail) principal;System.out.println(myTUserDetail); // 使用Jwt生成token并将用户的id传入String token jwtUtil.generateToken(myTUserDetail.getUsers().getId());redisTemplate.opsForValue().set(UserLogin: myTUserDetail.getUsers().getId(),JSON.toJSONString(myTUserDetail),60, TimeUnit.MINUTES);return token;}至此我们就完成了用户登录的全过程。要注意我们需要放行的路径验证码的生成路径用户登录路径、重置密码的路径等都需要进行放行。 由于数据库中还没有数据所以我先在test测试中生成一条数据再在前端进行登录 前端登录的结果如下 至此我们的登录功能就实行了。在下篇文章中我会实现其他的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高端自适应网站国内简约网站设计

移动互联网时代,Facebook作为全球最大的社交媒体平台之一,早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现,则为Facebook营销注入了新的活力,其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…

自适应网站教程wordpress分类显示文章列表

🏡《总目录》 目录 1,概述2,工作原理2.1,材料特性2.2,温度升高2.3,温度降低2.4,数学模型3,结构特点3.1,磁芯3.2,线圈3.3,骨架3.4,绝缘材料4,工艺流程4.1,材料准

沂水县的旅游景区的网站建设企业为什么要建立自己的网站

如果你正在考虑使用JavaScript进行后端开发,你会听到“Node.js”这个术语。Node通常与开发功能强大的web服务器联系在一起。 但 Node.js 究竟是什么?它是和 Angular 一样的 JavaScript 框架吗?它是一种编程语言吗?它是 JavaScrip…

凡科建站模板安徽天长建设局网站

登陆时间:2019-10-21实现难度:★★★☆☆☆请求链接:https://passport.bilibili.com/login实现目标:模拟登陆哔哩哔哩,攻克滑动验证码涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用完整代码&…

asp.net网站建设项目实战 董义革网站排名做不上去

一、搭建环境 1、安装Slenium pip install selenium 2、安装浏览器驱动-》查询浏览器版本-》下载对应版本驱动-》在path路径中配置(浏览器更新需要重新下载) pip install webdriver -helper(自动化)python3.9以上 pip install 安…

青岛高品质网站建设做亚马逊一个月挣10万

1185 威佐夫游戏 V2 有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁…

石家庄货运做网站公司河间做网站 申梦网络

一、什么是计算机视觉 定义:计算机视觉(Computer vision)是⼀⻔研究如何使机器“看”的科学,更 进⼀步的说,就是指⽤摄影机和计算机代替⼈眼对⽬标进⾏识别、跟踪和测量 等,⽤计算机处理成为更适合⼈眼观察…

苏州专业建站当阳seo外包

onFocus事件就是当光标落在文本框中时发生的事件。 onBlur事件是光标失去焦点时发生的事件。 可以编如下例子 1.html <HTML> <HEAD> <TITLE>使用onBlur事件处理程序</TITLE> </HEAD> <BODY BGCOLOR"lavender"> <FORM name&qu…

企业成品网站模板多语言网站制作

配置蓝牙终端定位示例 组网图形 图1 配置蓝牙终端定位示例组网图 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件 业务需求 商场管理员希望在已有WLAN覆盖业务的基础上&#xff0c;使用蓝牙定位技术&#xff0c;根据顾客所在的位置&#xff0c;提供商场导航、导…

网站建设用模板好吗体验营销策划方案

-- 获取当天日期 -- 2023-06-20 select curdate();-- 获取当天年月日时分秒 select now();-- 日期运算 -- 2024-06-20 17:04:17 select date_add(now(),interval 1 year);-- 日期比较 -- 0 select datediff(now(),now());-- 日期MySQL对于日期类型数据如何查询 -- 获取指定日期…

网站建设鼠标点击变色怎么弄网站备案 密码找回

我使用中英互译的方法来制作本次笔记&#xff0c;课程来自网上精品资源 VSCode相关快捷键 选择文件夹和拖拽文件夹来打开 使用&#xff01;加enter&#xff08;回车&#xff09;&#xff0c;输入默认模板 <!DOCTYPE html> <html lang"en"> <head&…

php网站建设案例教程视频教程帮助传销做网站会不会判刑

**单片机设计介绍&#xff0c;1650【毕设课设】基于STM32两轮自平衡小车系统设计与控制 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序文档 六、 文章目录 一 概要 主控芯片用的是100脚的STM32F103VET6&#xff0c;陀螺仪用的是MPU6050&#xff0c;电机…

社交做的最好的网站有哪些网站建设技术服务的方式是什么意思

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 代码写法&#xff1a; &#xff08;要求 JDK 1.8 或 1.8 以上&#xff09; package gentle.entity;import lombok.Data; /**** auth…

网站开发英文参考文献WordPress二级目录文章404

来源&#xff1a;兴业证券 ▌5G:大通信容量及超低延时&#xff0c;未来多项应用的基础5G:高工作频率以及频谱带宽带来高通信容量5G(5thgeneration)是指第五代移动电话通信标准。3GPP(第三代合作伙伴计划&#xff0c;电信标准化机构)将5G标准分为了NSA(非独立组网)和SA(独立组网…

网站开发难点企业宣传册设计

现在有dll模块A,dll模块B以及静态库C, C中有个全局Create()函数负责创建对象M,当第二次调用Create()的时候会增加引用计数&#xff0c;并返回原来创建的对象M。Relase()会减少引用计数。当引用计数为0时&#xff0c;销毁对象M。现在在模块A中创建的初始化对象M&#xff0c;模块…

男和男做的视频网站长春网站设计公司

文章目录 1. 前置条件2. 简介3. GitLab Kubernetes Agent 的部署3.1 启用 Agent 服务端3.2 创建 Agent 配置和清单仓库 4. 安装agent4.1 连接k8s集群4.2 在集群中部署4.3 修改资源清淡&#xff0c;调整pod的副本数 5. 思考 1. 前置条件 gitlab 14.5 专业版k8s集群helm客户端工…

珠海集团网站制作外包云服务器5元左右的

一个网友的迷惑&#xff1a; 我工作&#xff15;年了&#xff0c;一直做&#xff2a;&#xff12;&#xff25;&#xff25;的项目&#xff0c;前几天去面试&#xff0c;一个人问我JDBC有几种连接方式&#xff0c;这个问题这么多年以来我从来没有遇见过&#xff0c;不知道大家 …

0建设营销型网站步骤wordpress 改cms

EasyPOI是一款优秀的开源Java库&#xff0c;专为简化和优化Excel文件的导入导出操作而设计。下面&#xff0c;我会介绍EasyPOI在项目中使用EasyPOI&#xff0c;实现Excel文件的高效操作。帮助读者全面了解和掌握这一工具。 EasyPOI简介 官网&#xff1a; http://www.wupaas.co…

注册网站会员需填写企业网站的类型包括

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼山西省中等职业学校对口升学考试大纲信息技术类专业本考试大纲是以国家中等职业学校计算机专业教学指导方案为依据&#xff0c;以中等职业教育国家规划教材《物理(电工电子类)》(李广华、郝翠兰主编&#xff0c;电子工业出版社)、《…

网站建设公司官网软件开发是什么行业

文章目录1. 题目信息2. 解题2.1 暴力遍历2.2 二分查找1. 题目信息 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 你可以假设数组中无重复元素。 示例 1:输入…