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

news2025/5/24 23:10:05

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

目录

一、建立数据库

二、后端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.coloradmin.cn/o/1656868.html

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

相关文章

全球首发:抗量子、以太坊兼容测试网正式上线

量子计算机将有能力破解目前互联网上使用的主要加密算法&#xff0c;影响的领域包括银行应用程序、电子邮件服务和社交媒体平台。 2023年5月7日&#xff0c;QANplatform推出了全球首个兼容以太坊的抗量子区块链测试网&#xff0c;此举将使开发者能够使用任何编程语言来编写智能…

thinkphp6使用layui分页组件做分页效果

博主用的是layui2.9.8的版本&#xff0c;但这个版本的分页组件是动态效果的&#xff0c;但我需要的是静态分页&#xff0c;所以我自己封装了一个生成layui的分页代码生成代码。代码如下&#xff1a; 1、先创建文件&#xff0c;路径是extent/layui/LayuiPage.php&#xff0c;加…

Java实战:验证改进的哥德巴赫猜想

改进的哥德巴赫猜想&#xff08;Improved Goldbach’s Conjecture&#xff09;声称每个大于5的奇数都可以表示为三个素数之和。这个猜想是对原始哥德巴赫猜想的扩展&#xff0c;针对奇数的情况。原始哥德巴赫猜想是指每个大于2的偶数都可以表示为两个素数之和。尽管改进的哥德巴…

ROS 2边学边练(45)-- 构建一个能动的机器人模型

前言 在上篇中我们搭建了一个机器人模型(其由各个关节&#xff08;joint&#xff09;和连杆&#xff08;link&#xff09;组成)&#xff0c;此篇我们会通过设置关节类型来实现机器人的活动。 在ROS中&#xff0c;关节一般有无限旋转&#xff08;continuous&#xff09;,有限旋转…

el-dialog设置el-head固定

0 效果 1 代码 ::v-deep .adTextDetailDialogClass .el-dialog__body{max-height: calc(100vh - 150px);overflow: auto;border-top:1px solid #dfdfdf;border-bottom:1px solid #dfdfdf; } ::v-deep .adTextDetailDialogClass .el-dialog{position: fixed;height:fit-content;…

15-LINUX--线程的创建与同步

一.线程 1.线程的概念 线程是进程内部的一条执行序列或执行路径&#xff0c;一个进程可以包含多条线程。 2.线程的三种实现方式 ◼ 内核级线程&#xff1a;由内核创建&#xff0c;创建开销大&#xff0c;内核能感知到线程的存在 ◼ 用户级线程&#xff1a;线程的创建有用户空…

springboot 引入第三方bean

如何进行第三方bean的定义 参数进行自动装配

数据库中索引的底层原理和SQL优化

文章目录 关于索引B 树的特点MySQL 为什么使用 B 树&#xff1f; 索引分类聚簇索引 和 非聚簇索引覆盖索引索引的最左匹配原则索引与NULL索引的代价大表结构修改 SQL优化EXPLAIN命令选择索引列其它细节 关于索引 索引是一种用来加快查找效率的数据结构&#xff0c;可以简单粗暴…

探索黏土特效?推荐这三款软件!

在数字化时代&#xff0c;我们拥有无数的工具来释放我们的创造力和想象力。其中&#xff0c;黏土特效软件就是一种能够将你的照片或图像转化为可爱、生动的黏土动画的工具。这些软件以其独特的视觉效果和易于使用的特性&#xff0c;吸引了大量的用户。下面&#xff0c;我们将为…

gorm-sharding分表插件升级版

代码地址&#xff1a; GitHub - 137/gorm-sharding: Sharding 是一个高性能的 Gorm 分表中间件。它基于 Conn 层做 SQL 拦截、AST 解析、分表路由、自增主键填充&#xff0c;带来的额外开销极小。对开发者友好、透明&#xff0c;使用上与普通 SQL、Gorm 查询无差别.解决了原生s…

FreeRTOS学习 -- 任务相关API函数

一、任务创建和删除API函数 FreeRTOS 最基本的功能就是任务管理&#xff0c;而任务管理最基本的操作就是创建和删除任务。 FreeRTOS的任务创建和删除API函数如下&#xff1a; 1、函数 xTaskCreate() 此函数用来创建一个任务&#xff0c;任务需要 RAM 来保存于任务有关的状…

nginx的应用部署nginx

这里写目录标题 nginxnginx的优点什么是集群常见的集群什么是正向代理、反向代理、透明代理常见的代理技术正向代理反向代理透明代理 nginx部署 nginx nginx&#xff08;发音同enginex&#xff09;是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&…

每日一题 第九十七期 洛谷 [NOIP2000 提高组] 方格取数

[NOIP2000 提高组] 方格取数 题目背景 NOIP 2000 提高组 T4 题目描述 设有 N N N \times N NN 的方格图 ( N ≤ 9 ) (N \le 9) (N≤9)&#xff0c;我们将其中的某些方格中填入正整数&#xff0c;而其他的方格中则放入数字 0 0 0。如下图所示&#xff08;见样例&#xf…

同步电机原理解析

同步电机 同步带年纪&#xff0c;顾名思义无论负载如何&#xff0c;都能以恒定的速度运转&#xff0c;它以高效率著称 这种恒速特性是通过恒定磁场和旋转磁场的相互作用实现的&#xff0c;与其他电机一样&#xff0c;同步电机由定子和转子组成&#xff0c;定子铁芯由硅片层叠而…

STC8增强型单片机开发 【GPIO的理解⭐⭐】

目录 一、引言 二、GPIO概述 三、GPIO的功能 1. 输入功能&#xff1a; 2. 输出功能 四、GPIO的配置方法 1. 选择GPIO端口和引脚&#xff1a; 2. 设置GPIO模式&#xff1a; 3. 配置GPIO参数&#xff1a; 五、GPIO应用实例 1. 硬件连接&#xff1a; 2. 编程实现&…

2.1初识Spark

Spark于2009年诞生&#xff0c;最初是加州大学伯克利分校的研究项目。2013年加入Apache孵化器项目&#xff0c;2014年成为Apache顶级项目。Spark以内存内运算技术为核心&#xff0c;包含多个计算框架&#xff0c;成为大数据计算领域的后起之秀&#xff0c;打破了Hadoop的基准排…

2.外卖点餐系统(Java项目 springboot)

目录 0.系统的受众说明 1.系统功能设计 2.系统结构设计 3.数据库设计 3.1实体ER图 3.2数据表 4.系统实现 4.1用户功能模块 4.2管理员功能模块 4.3商家功能模块 4.4用户前台功能模块 4.5骑手功能模块 5.相关说明 新鲜运行起来的项目&#xff1a;如需要源码数据库…

如何防止源代码泄露?彻底解决源代码防泄密的方法

SDC沙盒系统&#xff1a;数据安全的守护者 SDC沙盒系统&#xff0c;为研发型企业设计&#xff0c;实现了对数据的代码级保护&#xff0c;同时不影响工作效率和正常使用。系统通过自动加密敏感数据&#xff0c;并配合多种管控机制&#xff0c;有效防止了数据的泄露。 涉密可信…

Python专题:五、条件语句

流程控制语句 count&#xff08;&#xff09;字符串计数 句尾\分行写码 运行输入cmd 输入Python 回车进入shell python 解释器 shell模式 再给x1,没有结果出来 if条件语句关键词&#xff0c;x>5条件表达式&#xff0c;&#xff1a;条件结束&#xff0c;四个空格&#x…

压缩机继电器EOCRDS-30NY7Q升级后型号:EOCRDS3-30S

EOCR-DS3系列型号&#xff1a; EOCRDS3-05S EOCRDS-05S EOCRDS1-05S EOCRDS3-30S EOCRDS-30S EOCRDS1-30S EOCRDS3-60S EOCRDS-60S EOCRDS1-60S EOCRDS3-05W EOCRDS-05W EOCRDS1-05W EOCRDS3-30W EOCRDS-30W EOCRDS1-30W EOCRDS3-60W EOCRDS-60W EOCRDS1-60W EOCR-DS3T-…