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)
    //    为了返回数据
    @ResponseBody
    public R error(Exception e) {
        e.printStackTrace();
        return R.error().message("执行全局异常处理");

    }
    //    特定异常处理---先找特殊异常处理然后再找全局异常处理
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public R error(ArithmeticException e){
        e.printStackTrace();
        return R.error().message("执行了ArithmeticException异常");
    }

    //    自定义异常处理--在编写代码的时候如果觉得这些代码可能会出现异常那么就要try catch
    @ExceptionHandler(XiyangException.class)
    @ResponseBody
    public 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 {
    //    属性名
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("gmtCreate",new Date(),metaObject);
        this.setFieldValByName("gmtModified",new Date(),metaObject);
    }

    @Override
    public 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;
    @Override
    public 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=GBK
		if (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() {

				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					return true;
				}

				@Override
				public void verify(String host, SSLSocket ssl)
						throws IOException {
				}

				@Override
				public void verify(String host, X509Certificate cert)
						throws SSLException {
				}

				@Override
				public 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 block
			e.printStackTrace();
		} catch (SocketTimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.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) {
//        没有就是false
        if (StringUtils.isEmpty(jwtToken)) return false;
        try {
//            不是有效也是false
            Jwts.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 {
    @Bean
    public 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 {
    @Autowired
    private UcenterMemberService ucenterMemberService;

    @ApiOperation(value = "登录")
    @PostMapping("login")
    public R loginUser(@RequestBody LoginVo loginVo){
//        前端传递过来必须带有手机号和密码
//        登录的时候要返回token
        String token = ucenterMemberService.login(loginVo);
        return R.ok().data("token",token);
    }

    @ApiOperation(value = "注册")
    @PostMapping("register")
    public R registerUser(@RequestBody RegisterVo registerVo){
//        前端传递过来必须带有验证码、手机、昵称和密码
//        登录的时候要返回token
        ucenterMemberService.register(registerVo);
        return R.ok();
    }

    @ApiOperation(value = "根据token得到用户信息")
    @GetMapping("getMemberInfo")
    public R getMemberInfo(HttpServletRequest request){
        try{
// 得到token
            String 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 {

    @Autowired
    private UcenterMemberService ucenterMemberService;

//    1.显示微信二维码
    @GetMapping("login")
    public String genWxCode(){
//       微信开放平台授权baseUrl--用于拼接url
        String 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和openid
            String 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保存到accessTokenInfo
            String 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);
//            发送请求得到个人信息保存到userInfo
                String 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;

    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    public 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-item
          class="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-item
          class="input-prepend"
          prop="password"
          :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
        >
          <div>
            <el-input
              type="password"
              placeholder="密码"
              v-model="user.password"
            />
            <i class="iconfont icon-password" />
          </div>
        </el-form-item>
        <div class="btn">
          <input
            type="button"
            class="sign-in-button"
            value="登录"
            @click="submitLogin()"
          />
        </div>
      </el-form>
      <!-- 更多登录方式 -->
      <div class="more-sign">
        <h6>社交帐号登录</h6>
        <ul>
          <li>
            <a
              id="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.memberInfo
            cookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:"localhost"})
          })
          // 提示登录成功
          this.$message({
            type: "success",
            message: "登录成功",
          });
          // 5.跳转路由
          this.$router.push({ path: "/" });
        }
      });
    },
    checkPhone (rule, value, callback) {
        //debugger
        if (!(/^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-item
          class="input-prepend restyle"
          prop="nickname"
          :rules="[
            {
              required: true,
              message: '请输入你的昵称',
              trigger: 'blur'
            }
          ]"
        >
          <div>
            <el-input
              type="text"
              placeholder="你的昵称"
              v-model="params.nickname"
            />
            <i class="iconfont icon-user" />
          </div>
        </el-form-item>
        <el-form-item
          class="input-prepend restyle no-radius"
          prop="mobile"
          :rules="[
            { required: true, message: '请输入手机号码', trigger: 'blur' },
            {
              validator: checkPhone,
              trigger: 'blur'
            }
          ]"
        >
          <div>
            <el-input
              type="text"
              placeholder="手机号"
              v-model="params.mobile"
            />
            <i class="iconfont icon-phone" />
          </div>
        </el-form-item>
        <el-form-item
          class="input-prepend restyle no-radius"
          prop="code"
          :rules="[
            { required: true, message: '请输入验证码', trigger: 'blur' }
          ]"
        >
          <div
            style="width: 100%;display: block;float: left;position: relative"
          >
            <el-input type="text" placeholder="验证码" v-model="params.code" />
            <i class="iconfont icon-phone" />
          </div>
          <div
            class="btn"
            style="position:absolute;right: 0;top: 6px;width:40%;"
          >
            <a
              href="javascript:"
              type="button"
              @click="getCodeFun()"
              :value="codeTest"
              style="border: none;background-color: none"
              >{{ codeTest }}</a
            >
          </div>
        </el-form-item>
        <el-form-item
          class="input-prepend"
          prop="password"
          :rules="[
            {
              required: true,
              message: '请输入密码',
              trigger: 'blur'
            }
          ]"
        >
          <div>
            <el-input
              type="password"
              placeholder="设置密码"
              v-model="params.password"
            />
            <i class="iconfont icon-password" />
          </div>
        </el-form-item>
        <div class="btn">
          <input
            type="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>
            <a
              id="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 = false
        this.timeDown()
    },
    // 超时设计
    timeDown() {
        let result = setInterval(() => {
            --this.second;
            // 把时间值给替换掉"获取验证码"
            this.codeTest = this.second
            if (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) {
        //debugger
        if (!(/^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>
              <a
                href="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">
                <input
                  type="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() {
    // 获取微信扫描后端传递过来在路径上的token
    this.token = this.$route.query.token
    if(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.memberInfo
        cookie.set("guli_ucenter",JSON.stringify(this.loginInfo),{domain:"localhost"})
      })
    },
    // 显示用户信息
    showInfo() {
      // 得到的是字符串,要变为json
      var userStr = cookie.get("guli_ucenter");
      if (userStr) {
        // 把字符串变为json
        this.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.mfbz.cn/a/607113.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

幻兽帕鲁专用服务器怎样买省钱便宜?一个月30元

在数字娱乐的浪潮中&#xff0c;幻兽帕鲁Palworld以其独特的魅力吸引了无数玩家的目光。想要拥有流畅、稳定的游戏体验&#xff0c;一台专属的游戏服务器是必不可少的。而如何以最经济的价格购买到高品质的服务器&#xff0c;正是玩家们最关心的问题。腾讯云服务器性价比是很高…

每日Attention学习6——Context Aggregation Module

模块出处 [link] [code] [IJCAI 22] Boundary-Guided Camouflaged Object Detection 模块名称 Context Aggregation Module (CAM) 模块作用 增大感受野&#xff0c;全局特征提取 模块结构 模块代码 import torch import torch.nn as nn import torch.nn.functional as Fcla…

Anaconda安装和深度学习环境的安装(TensorFlow、Pytorch)

换了新电脑&#xff0c;重新装一下anaconda这些编程环境。好久没装过了&#xff0c;自己也需要查查资料&#xff0c;然后记录一下&#xff0c;分享给别人。 目标&#xff0c;三个环境&#xff1a;1.anaconda基础环境&#xff08;包含xgboost和lightgbm&#xff09;&#xff0c…

卫星通信现状与展望三 -- 分类总结及6G应用

作者:私语茶馆 卫星通信分类总结及6G应用 一、卫星轨道类型 卫星按照轨道距离地面的距离主要分为以下几种: 卫星轨道类型 卫星用途 轨道高度 VLEO(Very Low Earth Orbit) 对地观测、通信

Python中使用tkinter模块和类结构的结合使用举例——编写制作一个简单的加数GUI界面

Python中使用tkinter模块和类结构的结合使用举例——编写制作一个简单的加数GUI界面 这里写目录标题 Python中使用tkinter模块和类结构的结合使用举例——编写制作一个简单的加数GUI界面一、tkinter模块和类的简述1.1 tkinter的简要介绍1.2 类结构的简要介绍 二、基于类机构和t…

成本降低 90%,出海社交平台 Typing 基于 Databend 的大数据探

Typing&#xff08;输入中科技&#xff09;成立于 2022 年&#xff0c;是一家主要面向东南亚、拉美、中东等海外地区提供社交平台的出海企业。其社交平台类似于国内的 Soul、陌陌等&#xff0c;提供视频直播、语音聊天室、短视频、生活分享、文字聊天等社交功能&#xff0c;注册…

【C++】零钱兑换的始端---柠檬水找零

欢迎来CILMY23的博客 本篇主题为 零钱兑换的始端---柠檬水找零 个人主页&#xff1a;CILMY23-CSDN博客 个人专栏系列&#xff1a; Python | C | C语言 | 数据结构与算法 感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言&#xff1a; 柠檬水找…

2024年最新【SpringBoot2】开发实用篇-测试_springboot2 test(1),2024年最新2024春招BAT面试真题详解

既有适合小白学习的零基础资料&#xff0c;也有适合3年以上经验的小伙伴深入学习提升的进阶课程&#xff0c;涵盖了95%以上软件测试知识点&#xff0c;真正体系化&#xff01; 由于文件比较多&#xff0c;这里只是将部分目录截图出来&#xff0c;全套包含大厂面经、学习笔记、…

吸血鬼崛起v rising皮革获取教程 v rising皮革机怎么获得

《V Rising》是一款由Stunlock Studios公司制作并发行的生存建造类游戏&#xff0c;以“吸血鬼”为题材。中文名为“吸血鬼崛起”。在游戏中&#xff0c;打boss可以获得许多掉落材料&#xff0c;有些材料需要合成&#xff0c;而制作皮革则需要使用皮革机。下面就为大家介绍一下…

利用大语言模型(KIMI)生成OPC UA 信息模型

在大语言模型没有出现之前&#xff0c;人们更倾向使用图形化工具或者基于窗口的软件来构建信息模型&#xff0c;图形化工具能够直观地表达信息模型中各元素之间的相互关系。但是图形化工具也有缺点&#xff0c;当描述一个复杂的信息模型时&#xff0c;图形会变得非常复杂和庞大…

如何通过OMS加快大表迁移至OceanBase

OMS&#xff0c;是OceanBase官方推出的数据迁移工具&#xff0c;能够满足众多数据迁移场景的需求&#xff0c;现已成为众多用户进行数据迁移同步的重要工具。OMS不仅支持多种数据源&#xff0c;还具备全量迁移、增量同步、数据校验等功能&#xff0c;并能够对分表进行聚合操作&…

豪投巨资,澳大利亚在追逐海市蜃楼吗?

澳大利亚政府正在积极投资于量子计算领域。继2021年向量子技术投资逾1亿澳元后&#xff0c;2023年5月&#xff0c;该国发布了首个国家量子战略&#xff0c;详细阐述了如何把握量子技术的未来及保持全球领先地位。 澳大利亚的国家量子战略概述 原文链接&#xff1a; https://ww…

jQuery-1.语法、选择器、节点操作

jQuery jQueryJavaScriptQuery&#xff0c;是一个JavaScript函数库&#xff0c;为编写JavaScript提供了更高效便捷的接口。 jQuery安装 去官网下载jQuery&#xff0c;1.x版本练习就够用 jQuery引用 <script src"lib/jquery-1.11.2.min.js"></script>…

力扣HOT100 - 4. 寻找两个正序数组的中位数

解题思路&#xff1a; 两个数组合并&#xff0c;然后根据奇偶返回中位数。 class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m nums1.length;int n nums2.length;int[] nums new int[m n];if (m 0) {if (n % 2 0) return (nums2…

游戏专用设备指纹方案解析

如同人类拥有独一无二的指纹&#xff0c;设备也有设备的指纹&#xff0c;我们可以把设备指纹理解为设备的唯一识别码。 构建设备指纹需要采集设备硬件信息、软件信息、环境信息、网络信息等维度信息&#xff0c;进行加密/压缩&#xff0c;再通过算法处理&#xff0c;赋予设备唯…

手机视频提取gif怎么操作?分享这个方法不能错过!

随着网络的发展动态gif表情包已经是人们交流的重要部分了。想要通过手机来实现视频转换gif的操作&#xff0c;还不想下载软件的情况下。可以通过使用手机端的视频转gif工具-GIF中文网&#xff0c;无需下载软件。手机端轻松一键就能在线实现视频提取gif的操作。一起来看看具体的…

【ETAS CP AUTOSAR工具链】RTA-OS基本概念与开发流程

RTA-OS基于早期ETAS操作系统的成熟技术&#xff0c;迄今为止&#xff0c;已在全球超过3.5亿个ECU中使用。RTA-OS是一个可静态配置的抢占式实时操作系统(RTOS)&#xff0c;它常被用于资源受限但有着高性能要求的方案中。内核的实现不仅遵循了AUTOSAR R3.x、R4.0、R4.1、R4.2、R4…

【stomp 实战】spring websocket 接收消息源码分析

后台消息的发送过程&#xff0c;我们通过spring websocket用户消息发送源码分析已经了解了。我们再来分析一下后端接收消息的过程。这个过程和后端发送消息过程有点类似。 前端发送消息 前端发送消息给服务端的示例如下&#xff1a; 发送给目的/app/echo一个消息。 //主动发…

英码科技推出昇腾系列AI加速卡:专为视频解析与模型推理场景打造,更具成本竞争力!

当前&#xff0c;人工智能的发展已进入加速渗透千行百业的阶段&#xff0c;算力已然成为数字化转型关键的新质生产力。随着国际挑战的加剧&#xff0c;国产算力的发展趋势愈发明显&#xff0c;市场需求也呈现出激增的态势。在这一大背景下&#xff0c;华为昇腾以其强大的技术实…

GaussianBody:基于3D高斯散射的服装人体重建

GaussianBody: Clothed Human Reconstruction via 3d Gaussian Splatting GaussianBody&#xff1a;基于3D高斯散射的服装人体重建 Mengtian Li1,2,3, Shengxiang Yao1, Zhifeng Xie1,3,2, Keyu Chen4,2, Yu-Gang Jiang2 李梦田 1,2,3 、姚胜祥 1 、谢志峰 1,3, 2 、陈科宇 4, …
最新文章