springboot+vue实现登录注册,短信注册以及微信扫描登录

说明:微信扫描登录需要微信注册--要钱,感谢尚硅谷提供的免费接口;短信注册需要阿里云的注册很麻烦并且短信费,没有接口,所以不打算实现,不过能做出效果。

目录

一、建立数据库

二、后端idea实现接口

1.新建项目

2.引入依赖

3.application.properties

4.一键生成mybatis

5.新建工具类

6.实体类

7.controller文件

8.service层

三、前端vue实现交互

1.安装依赖

2.新建工具类

3.新建api类

4.新建页面

5.路由

四、最终结果

一、建立数据库

create databases db2024;
use db2024
DROP TABLE IF EXISTS `ucenter_member`;
CREATE TABLE `ucenter_member` (`id` char(19) NOT NULL COMMENT '会员id',`openid` varchar(128) DEFAULT NULL COMMENT '微信openid',`mobile` varchar(11) DEFAULT '' COMMENT '手机号',`password` varchar(255) DEFAULT NULL COMMENT '密码',`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',`sex` tinyint(2) unsigned DEFAULT NULL COMMENT '性别 1 女,2 男',`age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',`avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',`sign` varchar(100) DEFAULT NULL COMMENT '用户签名',`is_disabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用,  0(false)未禁用',`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
INSERT INTO `ucenter_member` VALUES ('1', null, '13700000001', '96e79218965eb72c92a549dd5a330112', '小三123', '1', '5', 'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132', null, '0', '0', '2024-01-01 12:11:33', '2024-11-08 11:56:01');

通过上面的步骤就实现了数据库的建立和表的数据。

二、后端idea实现接口

1.新建项目

2.引入依赖

在pom.xml文件中把下面的代码赋值进去

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><!--        这里要是2.2.1,这样才能对应上tomcat--><version>2.2.1.RELEASE</version><relativePath/></parent><groupId>com.xiyang.generator</groupId><artifactId>generator_method1</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!--        如果这里启动版本不对那么tomcat的版本也就和以前不一样了,不能是3.0以上,为2.0--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.5</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.0</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency><!--httpclient--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.1</version></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.2</version></dependency><!-- JWT --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><!--swagger ui--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.7.0</version></dependency><!--日期时间工具--><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><!--tomcat10(包括10)之后需要的依赖--><!--jsp的依赖--><dependency><groupId>jakarta.servlet.jsp</groupId><artifactId>jakarta.servlet.jsp-api</artifactId><version>3.0.0</version><scope>provided</scope></dependency><!--jar包的依赖--><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>5.0.0</version><scope>provided</scope></dependency><!--下面的依赖,是因为Java版本不对解决Handler dispatch failed;nested exception is java.lang.NoClassDefFoundError:javax/xml/bind/DatatypeConverter--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-core</artifactId><version>2.3.0</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency></dependencies></project>

3.application.properties

在resources中建立文件

# 必须是这个端口
server.port=8160
spring.application.name=generator
# mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db2024?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
#mybatis
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#??mapper xml,这里看自己的路径是什么,别和我一模一样
mybatis-plus.mapper-locations=classpath:com/xiyang/generator/mapper/xml/*.xml# 微信 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信密钥 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信 url
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback

4.一键生成mybatis

目录结构为,如果没有生成就看我的其他一篇博客:mybatis-generator之一键生成:两种方法-CSDN博客

5.新建工具类

①exception统一异常处理类,里面有两个文件GlobalExceptionHandler和XiyangException

package com.xiyang.generator.utils.exception;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;自定义异常处理
@Data
@AllArgsConstructor
@NoArgsConstructor
public class XiyangException extends RuntimeException{private Integer code; //状态码private String msg;   //信息
}
package com.xiyang.generator.utils.exception;import com.xiyang.generator.utils.R;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** 统一异常处理类*/
@ControllerAdvice
public class GlobalExceptionHandler {//    出现全局异常就执行@ExceptionHandler(Exception.class)//    为了返回数据@ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message("执行全局异常处理");}//    特定异常处理---先找特殊异常处理然后再找全局异常处理@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic R error(ArithmeticException e){e.printStackTrace();return R.error().message("执行了ArithmeticException异常");}//    自定义异常处理--在编写代码的时候如果觉得这些代码可能会出现异常那么就要try catch@ExceptionHandler(XiyangException.class)@ResponseBodypublic R error(XiyangException e){e.printStackTrace();return R.error().code(e.getCode()).message(e.getMsg());}
}

②handler文件夹中有一个MyMetaObjectHandler,其作用就是实现数据库中时间代码的自动生成

package com.xiyang.generator.utils.handler;//自动创建时间类import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;//自动填充
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {//    属性名@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("gmtCreate",new Date(),metaObject);this.setFieldValByName("gmtModified",new Date(),metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("gmtModified",new Date(),metaObject);}
}

③ConstantPropertiesUtil用于读取application.properties中的属性

package com.xiyang.generator.utils;import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class ConstantPropertiesUtil implements InitializingBean {@Value("${wx.open.app_id}")private String appId;@Value("${wx.open.app_secret}")private String appSecret;@Value("${wx.open.redirect_url}")private String redirectUrl;public static String WX_OPEN_APP_ID;public static String WX_OPEN_APP_SECRET;public static String WX_OPEN_REDIRECT_URL;@Overridepublic void afterPropertiesSet() throws Exception {WX_OPEN_APP_ID = appId;WX_OPEN_APP_SECRET = appSecret;WX_OPEN_REDIRECT_URL = redirectUrl;}
}

④HttpClientUtils这里类用于实现微信扫描登录,这个是固定写法

package com.xiyang.generator.utils;import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;/***  依赖的jar包有:commons-lang-2.6.jar、httpclient-4.3.2.jar、httpcore-4.3.1.jar、commons-io-2.4.jar* @author zhaoyb**/
public class HttpClientUtils {public static final int connTimeout=10000;public static final int readTimeout=10000;public static final String charset="UTF-8";private static HttpClient client = null;static {PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();cm.setMaxTotal(128);cm.setDefaultMaxPerRoute(128);client = HttpClients.custom().setConnectionManager(cm).build();}public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);}public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);}public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,SocketTimeoutException, Exception {return postForm(url, params, null, connTimeout, readTimeout);}public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,SocketTimeoutException, Exception {return postForm(url, params, null, connTimeout, readTimeout);}public static String get(String url) throws Exception {return get(url, charset, null, null);}public static String get(String url, String charset) throws Exception {return get(url, charset, connTimeout, readTimeout);}/*** 发送一个 Post 请求, 使用指定的字符集编码.** @param url* @param body RequestBody* @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3* @param charset 编码* @param connTimeout 建立链接超时时间,毫秒.* @param readTimeout 响应超时时间,毫秒.* @return ResponseBody, 使用指定的字符集编码.* @throws ConnectTimeoutException 建立链接超时异常* @throws SocketTimeoutException  响应超时* @throws Exception*/public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)throws ConnectTimeoutException, SocketTimeoutException, Exception {HttpClient client = null;HttpPost post = new HttpPost(url);String result = "";try {if (StringUtils.isNotBlank(body)) {HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));post.setEntity(entity);}// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}post.setConfig(customReqConf.build());HttpResponse res;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(post);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(post);}result = IOUtils.toString(res.getEntity().getContent(), charset);} finally {post.releaseConnection();if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}return result;}/*** 提交form表单** @param url* @param params* @param connTimeout* @param readTimeout* @return* @throws ConnectTimeoutException* @throws SocketTimeoutException* @throws Exception*/public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,SocketTimeoutException, Exception {HttpClient client = null;HttpPost post = new HttpPost(url);try {if (params != null && !params.isEmpty()) {List<NameValuePair> formParams = new ArrayList<NameValuePair>();Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);post.setEntity(entity);}if (headers != null && !headers.isEmpty()) {for (Entry<String, String> entry : headers.entrySet()) {post.addHeader(entry.getKey(), entry.getValue());}}// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}post.setConfig(customReqConf.build());HttpResponse res = null;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(post);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(post);}return IOUtils.toString(res.getEntity().getContent(), "UTF-8");} finally {post.releaseConnection();if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}}/*** 发送一个 GET 请求** @param url* @param charset* @param connTimeout  建立链接超时时间,毫秒.* @param readTimeout  响应超时时间,毫秒.* @return* @throws ConnectTimeoutException   建立链接超时* @throws SocketTimeoutException   响应超时* @throws Exception*/public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)throws ConnectTimeoutException,SocketTimeoutException, Exception {HttpClient client = null;HttpGet get = new HttpGet(url);String result = "";try {// 设置参数Builder customReqConf = RequestConfig.custom();if (connTimeout != null) {customReqConf.setConnectTimeout(connTimeout);}if (readTimeout != null) {customReqConf.setSocketTimeout(readTimeout);}get.setConfig(customReqConf.build());HttpResponse res = null;if (url.startsWith("https")) {// 执行 Https 请求.client = createSSLInsecureClient();res = client.execute(get);} else {// 执行 Http 请求.client = HttpClientUtils.client;res = client.execute(get);}result = IOUtils.toString(res.getEntity().getContent(), charset);} finally {get.releaseConnection();if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {((CloseableHttpClient) client).close();}}return result;}/*** 从 response 里获取 charset** @param ressponse* @return*/@SuppressWarnings("unused")private static String getCharsetFromResponse(HttpResponse ressponse) {// Content-Type:text/html; charset=GBKif (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {String contentType = ressponse.getEntity().getContentType().getValue();if (contentType.contains("charset=")) {return contentType.substring(contentType.indexOf("charset=") + 8);}}return null;}/*** 创建 SSL连接* @return* @throws GeneralSecurityException*/private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {try {SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {@Overridepublic boolean verify(String arg0, SSLSession arg1) {return true;}@Overridepublic void verify(String host, SSLSocket ssl)throws IOException {}@Overridepublic void verify(String host, X509Certificate cert)throws SSLException {}@Overridepublic void verify(String host, String[] cns,String[] subjectAlts) throws SSLException {}});return HttpClients.custom().setSSLSocketFactory(sslsf).build();} catch (GeneralSecurityException e) {throw e;}}public static void main(String[] args) {try {String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);//String str= get("https://localhost:443/ssl/test.shtml?name=12&page=34","GBK");/*Map<String,String> map = new HashMap<String,String>();map.put("name", "111");map.put("page", "222");String str= postForm("https://localhost:443/ssl/test.shtml",map,null, 10000, 10000);*/System.out.println(str);} catch (ConnectTimeoutException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SocketTimeoutException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

⑥JwtUtils这个类 属于是jwt生成token用的,安全机制,也是固定写法

package com.xiyang.generator.utils;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;public class JwtUtils {//    设置过期时间public static final long EXPIRE = 1000 * 60 * 60 * 24;//    设置密钥--这里随便什么都可以public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";//    两个参数就是要放到jwt中的主体信息public static String getJwtToken(String id, String nickname){String JwtToken = Jwts.builder()
//                头信息的内容.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")
//                设置过期时间.setSubject("user").setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//                主体中的信息有id和nickname.claim("id", id).claim("nickname", nickname)
//                使用加密算法等.signWith(SignatureAlgorithm.HS256, APP_SECRET).compact();
//        生成一个token字符串return JwtToken;}/*** 下面的两个方法都是来判断token是否有效* 判断token是否存在与有效* @param jwtToken* @return**/public static boolean checkToken(String jwtToken) {
//        没有就是falseif (StringUtils.isEmpty(jwtToken)) return false;try {
//            不是有效也是falseJwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 判断token是否存在与有效* 这里的参数的request在headle中* @param request* @return*/public static boolean checkToken(HttpServletRequest request) {try {String jwtToken = request.getHeader("token");if(StringUtils.isEmpty(jwtToken)) return false;Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 根据token字符串获取token字符串中的数据信息--这里只要得到id就行* @param request* @return*/public static String getMemberIdByJwtToken(HttpServletRequest request) {String jwtToken = request.getHeader("token");if (StringUtils.isEmpty(jwtToken)) return "";
//        得到用户信息Jws<Claims> claimsJws =Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
//        得到主体Claims claims = claimsJws.getBody();return (String) claims.get("id");}
}

⑦MD5,为了保护数据库中密码的安全,对密码的加密处理的处理方式就是MD5所以读取数据的时候要加密解密,就用这个固定类。

package com.xiyang.generator.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public final class MD5 {public static String encrypt(String strSrc) {try {char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8','9', 'a', 'b', 'c', 'd', 'e', 'f' };byte[] bytes = strSrc.getBytes();MessageDigest md = MessageDigest.getInstance("MD5");md.update(bytes);bytes = md.digest();int j = bytes.length;char[] chars = new char[j * 2];int k = 0;for (int i = 0; i < bytes.length; i++) {byte b = bytes[i];chars[k++] = hexChars[b >>> 4 & 0xf];chars[k++] = hexChars[b & 0xf];}return new String(chars);} catch (NoSuchAlgorithmException e) {e.printStackTrace();throw new RuntimeException("MD5加密出错!!+" + e);}}public static void main(String[] args) {System.out.println(MD5.encrypt("111111"));}}

⑧R统一返回结果给前端

package com.xiyang.generator.utils;import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.HashMap;
import java.util.Map;@Data
public class R {@ApiModelProperty(value = "是否成功")private Boolean success;@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String, Object> data = new HashMap<String, Object>();private R(){}public static R ok(){R r = new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage("成功");return r;}public static R error(){R r = new R();r.setSuccess(false);r.setCode(ResultCode.ERROR);r.setMessage("失败");return r;}public R success(Boolean success){this.setSuccess(success);return this;}public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(Map<String, Object> map){this.setData(map);return this;}
}

⑨ResultCode状态码

package com.xiyang.generator.utils;public interface ResultCode {public static Integer SUCCESS = 20000;public static Integer ERROR = 20001;
}

⑩swagger提示

package com.xiyang.generator.utils;import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;//swagger配置类
@Configuration
@EnableSwagger2
public class swaggerConfig {@Beanpublic Docket webApiConfig() {return new Docket(DocumentationType.SWAGGER_2).groupName("webApi").apiInfo(webApiInfo()).select().paths(Predicates.not(PathSelectors.regex("/admin/.*"))).paths(Predicates.not(PathSelectors.regex("/error.*"))).build();}private ApiInfo webApiInfo(){return new ApiInfoBuilder().title("登录-注册测试").description("本文档描述了登录注册接口定义").version("1.0").build();}
}

6.实体类

为了方便登录注册的时候要的变量,所以新建两个类在entities中新建文件夹vo,里面创建两个类LoginVo和registerVo。

package com.xiyang.generator.entity.vo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(value="登录对象", description="登录对象")
public class LoginVo {@ApiModelProperty(value = "手机号")private String mobile;@ApiModelProperty(value = "密码")private String password;
}
package com.xiyang.generator.entity.vo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(value="注册对象", description="注册对象")
public class RegisterVo {@ApiModelProperty(value = "昵称")private String nickname;@ApiModelProperty(value = "手机号")private String mobile;@ApiModelProperty(value = "密码")private String password;@ApiModelProperty(value = "验证码")private String code;
}

注意,一键生成的实体中对于主键要改为@TableId(value = "id", type = IdType.ID_WORKER_STR),对于生成的时间创建加上@TableField(fill = FieldFill.INSERT),而更新时间改为@TableField(fill = FieldFill.INSERT_UPDATE)。

7.controller文件

新建两个controller基本注册登录功能UcenterMemberController、微信扫描登录的WxApiController

package com.xiyang.generator.controller;import com.xiyang.generator.entity.UcenterMember;
import com.xiyang.generator.entity.vo.LoginVo;
import com.xiyang.generator.entity.vo.RegisterVo;
import com.xiyang.generator.service.UcenterMemberService;
import com.xiyang.generator.utils.JwtUtils;
import com.xiyang.generator.utils.R;
import com.xiyang.generator.utils.exception.XiyangException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
/*** <p>* 会员表 前端控制器* </p>** @author xiyang* @since 2024-05-09*/
@RestController
@RequestMapping("/educenter/member")
@CrossOrigin
@Api(description = "登录与注册")
public class UcenterMemberController {@Autowiredprivate UcenterMemberService ucenterMemberService;@ApiOperation(value = "登录")@PostMapping("login")public R loginUser(@RequestBody LoginVo loginVo){
//        前端传递过来必须带有手机号和密码
//        登录的时候要返回tokenString token = ucenterMemberService.login(loginVo);return R.ok().data("token",token);}@ApiOperation(value = "注册")@PostMapping("register")public R registerUser(@RequestBody RegisterVo registerVo){
//        前端传递过来必须带有验证码、手机、昵称和密码
//        登录的时候要返回tokenucenterMemberService.register(registerVo);return R.ok();}@ApiOperation(value = "根据token得到用户信息")@GetMapping("getMemberInfo")public R getMemberInfo(HttpServletRequest request){try{
// 得到tokenString memberId = JwtUtils.getMemberIdByJwtToken(request);UcenterMember member = ucenterMemberService.getById(memberId);return R.ok().data("memberInfo",member);}catch (Exception e){e.printStackTrace();throw new XiyangException(20001,"获取用户信息失败");}}
}
package com.xiyang.generator.controller;import com.google.gson.Gson;
import com.xiyang.generator.entity.UcenterMember;
import com.xiyang.generator.service.UcenterMemberService;
import com.xiyang.generator.utils.ConstantPropertiesUtil;
import com.xiyang.generator.utils.HttpClientUtils;
import com.xiyang.generator.utils.JwtUtils;
import com.xiyang.generator.utils.exception.XiyangException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;@CrossOrigin
@Controller //因为我们不要返回结果,所以这里是controller
//这里的路径必须是一样的
@RequestMapping("/api/ucenter/wx")
public class WxApiController {@Autowiredprivate UcenterMemberService ucenterMemberService;//    1.显示微信二维码@GetMapping("login")public String genWxCode(){
//       微信开放平台授权baseUrl--用于拼接urlString baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +"?appid=%s" +"&redirect_uri=%s" +"&response_type=code" +"&scope=snsapi_login" +"&state=%s" +"#wechat_redirect";
//          redirectUrl必须进行重新编码,防止错误格式String redirectUrl = ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL; //获取业务服务器重定向地址try {redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8"); //url编码} catch (UnsupportedEncodingException e) {throw new XiyangException(20001, e.getMessage());}String qrcodeUrl = String.format(baseUrl,ConstantPropertiesUtil.WX_OPEN_APP_ID,redirectUrl,"xiyang");return "redirect:" + qrcodeUrl;}//    2.微信扫码之后要跳转
//    必须是这样因为服务器就是这样写的,所以我们再重定向到本地的前端前台3000端口,有两个参数code和state@GetMapping("callback")public String callback(String code,String state){try{//        获取扫描人的信息然后把数据放到数据库中
//        ①拿到code值请求微信固定的地址,得到两个值access_token和openidString baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +"?appid=%s" +"&secret=%s" +"&code=%s" +"&grant_type=authorization_code";
//        拼接三个参数String accessTokenUrl = String.format(baseAccessTokenUrl,ConstantPropertiesUtil.WX_OPEN_APP_ID,ConstantPropertiesUtil.WX_OPEN_APP_SECRET,code);
//        拼接地址之后,发送请求得到access_token和openid保存到accessTokenInfoString accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//        但是只要里面的一部分,所以要把字符串只取部分并放到map中,使用gson工具Gson gson = new Gson();HashMap hashMap = gson.fromJson(accessTokenInfo, HashMap.class);String accessToken = (String)hashMap.get("access_token");String openid = (String)hashMap.get("openid");//            把数据放到数据库中,但是重复的就不用加入,通过openid来判断UcenterMember member = ucenterMemberService.getOpenIdMember(openid);if(member ==  null){ //表中没有相同的微信数据,那么就加//            ②通过得到的这两个值,再使用固定的方法去请求固定的地址,得到扫描人的信息
//              访问微信的资源服务器,获取用户信息String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +"?access_token=%s" +"&openid=%s";String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
//            发送请求得到个人信息保存到userInfoString userInfo = HttpClientUtils.get(userInfoUrl);HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
//            头像、昵称String nickname = (String)userInfoMap.get("nickname");String headimgurl = (String)userInfoMap.get("headimgurl");member = new UcenterMember();member.setOpenid(openid);member.setNickname(nickname);member.setAvatar(headimgurl);
//                 添加到数据库中ucenterMemberService.save(member);}
//            使用jwt把扫描信息放到token中,并把token放到路径中传递给前端页面String token = JwtUtils.getJwtToken(member.getId(), member.getNickname());//         重定向,因为我的前端端口是3000,所以这里就固定了return "redirect:http://localhost:3000?token="+token;}catch (Exception e){e.printStackTrace();throw new XiyangException(20001,"扫描获取信息失败");}}
}

8.service层

UcenterMemberService文件

package com.xiyang.generator.service;import com.xiyang.generator.entity.UcenterMember;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiyang.generator.entity.vo.LoginVo;
import com.xiyang.generator.entity.vo.RegisterVo;/*** <p>* 会员表 服务类* </p>** @author xiyang* @since 2024-05-09*/
public interface UcenterMemberService extends IService<UcenterMember> {String login(LoginVo loginVo);void register(RegisterVo registerVo);UcenterMember getOpenIdMember(String openid);
}

对应接口的实现UcenterMemberServiceImpl

package com.xiyang.generator.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xiyang.generator.entity.UcenterMember;
import com.xiyang.generator.entity.vo.LoginVo;
import com.xiyang.generator.entity.vo.RegisterVo;
import com.xiyang.generator.mapper.UcenterMemberMapper;
import com.xiyang.generator.service.UcenterMemberService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiyang.generator.utils.JwtUtils;
import com.xiyang.generator.utils.MD5;
import com.xiyang.generator.utils.exception.XiyangException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;/*** <p>* 会员表 服务实现类* </p>** @author xiyang* @since 2024-05-09*/
@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
//    因为验证码在数据库中,所以要把注入redis,但是我们没有实现短信验证
//    @Autowired
//    private RedisTemplate<String,String> redisTemplate;@Overridepublic String login(LoginVo loginVo) {String mobile = loginVo.getMobile();String password = loginVo.getPassword();if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){throw new XiyangException(20001,"手机号或密码为空");}//        按照手机号查询出一条记录QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();wrapper.eq("mobile",mobile);UcenterMember selectOne = baseMapper.selectOne(wrapper);if(selectOne == null){throw new XiyangException(20001,"该手机卡号未注册");}//        数据库中的密码是加密的,所以要把前端的密码进行加密再比较,使用工具类if(!selectOne.getPassword().equals(MD5.encrypt(password))){throw new XiyangException(20001,"密码错误");}if(selectOne.getIsDisabled()){throw new XiyangException(20001,"该用户被禁用");}//        要生成token并返回给前端String jwtToken = JwtUtils.getJwtToken(selectOne.getId(), selectOne.getNickname());return jwtToken;}@Overridepublic void register(RegisterVo registerVo) {String mobile = registerVo.getMobile();String password = registerVo.getPassword();String nickname = registerVo.getNickname();String code = registerVo.getCode();if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)|| StringUtils.isEmpty(nickname) ||StringUtils.isEmpty(code)){throw new XiyangException(20001,"有值为空");}
//        先判断数据库中是否有这条手机数据,有的话就不能进行注册QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();wrapper.eq("mobile",mobile);Integer count = baseMapper.selectCount(wrapper);
//        count.intValue()个数大于一就不行if(count.intValue() > 0){throw new XiyangException(20001,"该手机号已被注册");}//        因为没有使用redis,否则这里应该是
//        redisTemplate.opsForValue().get(mobile).equals(code);if(!code.equals("9527")){throw new XiyangException(20001,"验证码错误");}//        把数据放到数据库中UcenterMember member = new UcenterMember();member.setMobile(mobile);member.setNickname(nickname);member.setPassword(MD5.encrypt(password));member.setIsDisabled(false); //用户是否被禁用member.setAvatar("https://guli-file-190513.oss-cn-beijing.aliyuncs.com/avatar/default.jpg");baseMapper.insert(member);}@Overridepublic UcenterMember getOpenIdMember(String openid) {QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();wrapper.eq("openid",openid);UcenterMember member = baseMapper.selectOne(wrapper);return member;}
}

9.启动类

package com.xiyang.generator;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;@SpringBootApplication
@ComponentScan(basePackages = {"com.xiyang"})
@MapperScan("com.xiyang.generator.mapper")
public class Main {public static void main(String[] args) {SpringApplication.run(Main.class,args);}
}

通过上面的所有步骤就实现了扫描登录、正常注册登录的过程。

三、前端vue实现交互

你已经建立起来了vue的基本框架,所以下面的代码就是在这个框架上生成的

1.安装依赖

npm stall js-cookie;npm install element-ui;npm install --save vue-router;npm install axios

2.新建工具类

新建方法request.js用于请求后端端口

import Axios from "axios";
// 引入cookie
import cookie from "js-cookie";const service = Axios.create({//后端地址baseURL: "http://localhost:8160",timeout: 20000
});
// 3.这里作为所有请端口的父端口,这里写拦截器
service.interceptors.request.use(config => {//debugger--cookie中没有token就把token放到cookie中if (cookie.get("guli_token")) {config.headers["token"] = cookie.get("guli_token");}return config;},err => {return Promise.reject(err);}
);
export default service;

3.新建api类

①login用于请求后端登录地址

import request from "@/utils/request"// 登录相关接口
export default{// 请求注册接口,传递json参数loginUser(user){return request({url:`/educenter/member/login`,method: 'post',data: user})},// getMemberInfo获取token中的头信息获取用户的信息getMemberInfo(){return request({url:`/educenter/member/getMemberInfo`,method: 'get',})}
}

②register用于请求后端注册接口

import request from "@/utils/request"// 注册相关接口
export default{// 请求注册接口,传递json参数registerInfo(user){return request({url:`/educenter/member/register`,method: 'post',data: user})},
}

4.新建页面

①登录页面

<template><div class="main"><div class="title"><a class="active" href="/login">登录</a><span>·</span><a href="/register">注册</a></div><div class="sign-up-container"><el-form ref="userForm" :model="user"><el-form-itemclass="input-prepend restyle"prop="mobile":rules="[{required: true,message: '请输入手机号码',trigger: 'blur',},{ validator: checkPhone, trigger: 'blur' },]"><div><el-input type="text" placeholder="手机号" v-model="user.mobile" /><i class="iconfont icon-phone" /></div></el-form-item><el-form-itemclass="input-prepend"prop="password":rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"><div><el-inputtype="password"placeholder="密码"v-model="user.password"/><i class="iconfont icon-password" /></div></el-form-item><div class="btn"><inputtype="button"class="sign-in-button"value="登录"@click="submitLogin()"/></div></el-form><!-- 更多登录方式 --><div class="more-sign"><h6>社交帐号登录</h6><ul><li><aid="weixin"class="weixin"target="_blank"href="http://localhost:8160/api/ucenter/wx/login"><i class="iconfont icon-weixin" /></a></li><li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq" /></a></li></ul></div></div></div>
</template>
<script>
import "~/assets/css/sign.css";
import "~/assets/css/iconfont.css";
// 引入cookie
import cookie from 'js-cookie'
import loginApi from "../api/login";
export default {layout: "sign",data() {return {user: {mobile: "",password: "",},// 为了获取用户信息loginInfo: {},};},methods: {// 1.登录submitLogin() {loginApi.loginUser(this.user).then((response) => {if(response.data.success){// 2.得到token并放到cookie中,参数表示cookie名称、cookie内容,作用范围cookie.set("guli_token",response.data.data.token,{domain:"localhost"})// 4.通过token得到数据并把数据放到cookie中loginApi.getMemberInfo().then(response => {this.loginInfo = response.data.data.memberInfocookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:"localhost"})})// 提示登录成功this.$message({type: "success",message: "登录成功",});// 5.跳转路由this.$router.push({ path: "/" });}});},checkPhone (rule, value, callback) {//debuggerif (!(/^1[34578]\d{9}$/.test(value))) {return callback(new Error('手机号码格式不正确'))}return callback()}},
};
</script>
<style>
.el-form-item__error {z-index: 9999999;
}
</style>

②注册页面

<template><div class="main"><div class="title"><a href="/login">登录</a><span>·</span><a class="active" href="/register">注册</a></div><div class="sign-up-container"><el-form ref="userForm" :model="params"><el-form-itemclass="input-prepend restyle"prop="nickname":rules="[{required: true,message: '请输入你的昵称',trigger: 'blur'}]"><div><el-inputtype="text"placeholder="你的昵称"v-model="params.nickname"/><i class="iconfont icon-user" /></div></el-form-item><el-form-itemclass="input-prepend restyle no-radius"prop="mobile":rules="[{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkPhone,trigger: 'blur'}]"><div><el-inputtype="text"placeholder="手机号"v-model="params.mobile"/><i class="iconfont icon-phone" /></div></el-form-item><el-form-itemclass="input-prepend restyle no-radius"prop="code":rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]"><divstyle="width: 100%;display: block;float: left;position: relative"><el-input type="text" placeholder="验证码" v-model="params.code" /><i class="iconfont icon-phone" /></div><divclass="btn"style="position:absolute;right: 0;top: 6px;width:40%;"><ahref="javascript:"type="button"@click="getCodeFun()":value="codeTest"style="border: none;background-color: none">{{ codeTest }}</a></div></el-form-item><el-form-itemclass="input-prepend"prop="password":rules="[{required: true,message: '请输入密码',trigger: 'blur'}]"><div><el-inputtype="password"placeholder="设置密码"v-model="params.password"/><i class="iconfont icon-password" /></div></el-form-item><div class="btn"><inputtype="button"class="sign-up-button"value="注册"@click="submitRegister()"/></div><p class="sign-up-msg">点击 “注册” 即表示您同意并愿意遵守简书<br /><a target="_blank" href="http://www.jianshu.com/p/c44d171298ce">用户协议</a>和<a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a>。</p></el-form><!-- 更多注册方式 --><div class="more-sign"><h6>社交帐号直接注册</h6><ul><li><aid="weixin"class="weixin"target="_blank"href="http://localhost:8160/api/ucenter/wx/login"><i class="iconfont icon-weixin"/></a></li><li><a id="qq" class="qq" target="_blank" href="#"><i class="iconfont icon-qq"/></a></li></ul></div></div></div>
</template>
<script>
import "~/assets/css/sign.css";
import "~/assets/css/iconfont.css";
import registerApi from "../api/register";
export default {layout: "sign",data() {return {params: {mobile: "",code: "",nickname: "",password: ""},sending: true, //是否发送验证码second: 60, //倒计时间codeTest: "获取验证码"};},methods:{// 获取验证码getCodeFun(){// 防止多次点击if (!this.sending)return// 调用超时设计,并且不能再发送验证码this.sending = falsethis.timeDown()},// 超时设计timeDown() {let result = setInterval(() => {--this.second;// 把时间值给替换掉"获取验证码"this.codeTest = this.secondif (this.second < 1) {clearInterval(result);// 超时完之后又能发送验证码this.sending = true;//this.disabled = false;this.second = 60;this.codeTest = "获取验证码"}}, 1000);},// 注册方法--也就是把数据放到数据库中submitRegister(){registerApi.registerInfo(this.params).then(response => {//提示注册成功this.$message({type: 'success',message: "注册成功"})// 跳转路由this.$router.push({path: '/login'})})},checkPhone (rule, value, callback) {//debuggerif (!(/^1[34578]\d{9}$/.test(value))) {return callback(new Error('手机号码格式不正确'))}return callback()}}
};
</script>

③首页

<template><div class="in-wrap"><!-- 公共头引入 --><header id="header"><section class="container"><h1 id="logo"><!-- <a href="#" title="谷粒学院"><img src="~/assets/img/logo.png" width="100%" alt="谷粒学院" /></a> --></h1><div class="h-r-nsl"><ul class="nav"><router-link to="/" tag="li" active-class="current" exact><a>首页</a></router-link></ul><!-- / nav --><!-- 当用户进行登录之后,这里就不能这样显示了 --><ul class="h-r-login"><li v-if="!loginInfo.id" id="no-login"><a href="/login" title="登录"><em class="icon18 login-icon">&nbsp;</em><span class="vam ml5">登录</span></a>|<a href="/register" title="注册"><span class="vam ml5">注册</span></a></li><li v-if="loginInfo.id" id="is-login-one" class="mr10"><a id="headerMsgCountId" href="#" title="消息"><em class="icon18 news-icon">&nbsp;</em></a><q class="red-point" style="display: none">&nbsp;</q></li><li v-if="loginInfo.id" id="is-login-two" class="h-r-user"><a href="/ucenter" title><img:src="loginInfo.avatar"width="30"height="30"class="vam picImg"alt/><span id="userName" class="vam disIb">{{loginInfo.nickname}}</span></a><ahref="javascript:void(0);"title="退出"@click="logout()"class="ml5">退 出</a></li><!-- /未登录显示第1 li;登录后显示第2,3 li --></ul><aside class="h-r-search"><form action="#" method="post"><label class="h-r-s-box"><inputtype="text"placeholder="输入你想学的课程"name="queryCourse.courseName"value/><button type="submit" class="s-btn"><em class="icon18">&nbsp;</em></button></label></form></aside></div><aside class="mw-nav-btn"><div class="mw-nav-icon"></div></aside><div class="clear"></div></section></header></div>
</template>
<script>
// 引入cookie
import cookie from "js-cookie";
import loginApi from "../api/login";export default {data() {return {token: "",loginInfo: {id: "",age: "",avatar: "",mobile: "",nickname: "",sex: ""}};},created() {// 获取微信扫描后端传递过来在路径上的tokenthis.token = this.$route.query.tokenif(this.token){this.wxLogin()}else{this.showInfo();}},methods: {wxLogin(){// 先把token放到cookie中,然后再拦截,然后再请求信息cookie.set("guli_token",this.token,{domain:"localhost"})cookie.set("guli_ucenter",'',{domain:"localhost"})loginApi.getMemberInfo().then(response => {this.loginInfo = response.data.data.memberInfocookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:"localhost"})})},// 显示用户信息showInfo() {// 得到的是字符串,要变为jsonvar userStr = cookie.get("guli_ucenter");if (userStr) {// 把字符串变为jsonthis.loginInfo = JSON.parse(userStr);}},// 退出,清空cookie中值logout(){cookie.set("guli_token",'',{domain:"localhost"})cookie.set("guli_ucenter",'',{domain:"localhost"})this.$router.push({ path: "/" });}}
};
</script>

5.路由

路由自己看着来吧

import Vue from 'vue'
import Router from 'vue-router'
import App from './App.vue'Vue.use(Router)// 引入组件
import Home from './components/Home.vue';
import Login from './components/Login.vue';
import Register from './components/Register.vue';// 创建Vue Router实例
const router = new VueRouter({routes: [{ path: '/', component: Home },{ path: '/login', component: About },{ path: '/register', component: Register },],
});// 创建和挂载根实例
new Vue({router, // 使用router配置render: h => h(App),
}).$mount('#app');

四、最终结果

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

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

相关文章

【MySQL数据库开发设计规范】之基础规范

欢迎点开这篇文章&#xff0c;自我介绍一下哈&#xff0c;本人笔名姑苏老陈&#xff0c;是一个JAVA开发老兵。 本文收录于 《MySQL数据库开发设计规范》专栏中&#xff0c;该专栏主要分享一些关于MySQL数据库开发设计相关的技术规范文章&#xff0c;定期更新&#xff0c;欢迎关…

【图书推荐】《图神经网络基础、模型与应用实战》

本书目的 详解PyTorch 图神经网络基础理论、模型与十多个应用案例&#xff0c;带领读者掌握图神经网络在自然语言处理、计算机视觉、推荐系统、社交网络4个领域的应用开发方法&#xff0c;丰富读者利用深度学习算法解决实际问题的能力。 本书案例 图卷积网络实现图注意力网络…

马蹄集oj赛(双周赛第二十六次)

目录 斐波那契数列的组合 三国杀 数列分段 小码哥的跳棋游戏新编 能量供应 小码哥爱数字 最小串 小船过河 摘果子 泼墨淋漓 很重的枪 小码哥的布阵指挥 斐波那契数列的组合 #include<bits/stdc.h> using namespace std;// 斐波那契数列 1 1 2 3 5 8 13 21 34…

##08 数据加载与预处理:PyTorch的心脏

文章目录 前言深入理解`torch.utils.data`数据集(Dataset)数据加载器(DataLoader)实战演练:创建自定义数据集数据转换(Transform)数据加载总结前言 在深度学习的宇宙中,数据是燃料,模型是发动机。而在PyTorch的世界中,torch.utils.data是加注燃料的机器,保证了数据能够高…

C++ windows LOG4plus的使用

下载源码 https://sourceforge.net/projects/log4cplus/ 下载后再对应的目录下打开.sln文件 本次编译的是这个项目 新建一个项目 项目在test目录中 将头文件和lib如下添加 在项目中配置 注意源码编译的动态库的字符集和测试程序的字符集要一致&#xff0c;不然会报错 …

Google Pixel4手机刷机+Root+逆向环境详细教程

Google Pixel4手机刷机Root逆向环境配置详细教程 刷机工具下载 Windows10、Google Pixel4手机当前安卓10系统、adb工具、要刷的谷歌原生的Android11最新刷机包、安装google usb驱动、美版临时twrp-3.6.0_11-0-flame.img和美版永久twrp-installer-3.6.0_11-0-flame.zip、Magis…

【Matlab-动画-附源码】3分钟教你用Matlab做一个Lorenz动画

lorenz-x-y-z Lorenz三个维度数据 在科研工作中&#xff0c;经常需要将数据可视化以便更好地理解和传达研究成果。 但大家主要放静态图片&#xff0c;而视频或动画通常比静态图片更具吸引力和表现力。AE, Manim太难学&#xff0c;Matlab就可以用来制作动画。 在这篇博客中&…

vue 代码样式问题

部分电脑存在样式错乱问题&#xff0c;部分电脑样式正常。最后发现是样式写在 el-col 里面导致的。 注意&#xff1a;写样式不要放在 el-row 或者 el-row &#xff0c;导致部分电脑会出现莫名其妙的样式问题 <el-row class"detail"><el-col class"it…

「 网络安全常用术语解读 」通用配置枚举CCE详解

1. 背景介绍 NIST提供了安全内容自动化协议&#xff08;Security Content Automation Protocol&#xff0c;SCAP&#xff09;为漏洞描述和评估提供一种通用语言。SCAP组件包括&#xff1a; 通用漏洞披露(Common Vulnerabilities and Exposures, CVE)&#xff1a;提供一个描述…

80端口被占用,解决办法

1.首先打开控制面板&#xff08;可以在左下角搜索&#xff09; 2. 点击程序 3.点击启用或关闭Windows功能 4. 关闭Internet Information Services 下图是关闭后的样子 5.点击确定&#xff0c;然后重启 这样就解决了80端口被占用的问题。

离开大厂创业一年,从未受过这么大的打击!

大家好&#xff0c;我是程序员鱼皮。时间过得真快呀&#xff0c;转眼间我从腾讯出来、自主创业竟然已经整整一年了&#xff01;上周末也带团队同学们搞了场公司周年庆团建。 我自己是一个很喜欢、也很注重复盘总结的人&#xff0c;这么重要的时间&#xff0c;当然要对过去的一…

linux基础命令第二篇

雷迪斯and the乡亲们 欢迎你们来到 奇幻的编程世界 8.history命令 作用&#xff1a;查看和控制历史命令纪录 保存位置&#xff1a;&#xff1a;~/.bash_history &#xff08;1&#xff09;在启动终端的时候&#xff0c;会自动从&#xff1a;~/.bash_history 中读取历史命令…