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');

四、最终结果

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring BootVue.js是两个非常流行的开源框架,可以用来构建Web应用程序。使用Spring BootVue.js可以实现登录注册功能,具体步骤如下: 1. 创建Spring Boot项目 使用Spring Initializr创建一个新的Spring Boot项目,添加所需的依赖项,如Spring Security和Spring Data JPA。 2. 创建数据库表 使用MySQL或其他关系型数据库创建用户表,包括用户名、密码、电子邮件等字段。 3. 创建REST API 使用Spring Boot创建REST API,包括注册登录功能。注册API将用户信息保存到数据库中,登录API将验证用户凭据并生成JWT令牌。 4. 创建Vue.js应用程序 使用Vue CLI创建一个新的Vue.js应用程序,使用Vue Router和Vuex管理应用程序状态和路由。 5. 创建登录注册页面 使用Vue.js创建登录注册页面,包括表单和验证逻辑。在表单提交时,调用REST API进行用户验证和注册。 6. 集成JWT令牌 在Vue.js应用程序中集成JWT令牌,将令牌保存在本地存储中,并在每个请求中发送令牌。 7. 部署应用程序 将Spring Boot应用程序和Vue.js应用程序部署到服务器上,确保它们可以相互通信。 通过以上步骤,就可以使用Spring BootVue.js实现登录注册功能。 ### 回答2: Spring Boot是一个帮助开发者快速构建Web应用程序的开发框架,它提供了许多功能强大的插件和工具,可以轻松地搭建一个初始的项目结构。Vue.js 是流行的开源 JavaScript 框架,它提供了一个灵活可扩展的前端开发体验,可用于从小型单页面应用到大型企业级 Web 应用程序的构建。 在Spring Boot实现登录注册功能主要需要通过Spring Security对用户验证进行设置。首先,需要将Spring Security集成到Spring Boot中,从而进行认证和授权。为此,我们需要在项目中添加相关的依赖,并定义一个配置类来启用Spring Security。在配置类中,需要进行对用户验证和密码加密等相关设置。 注册功能的实现需要通过前端页面来进行用户信息的录入,并使用Vue.js进行表单提交。在Vue.js的基础上,我们可以使用axios向后端发送POST请求来提交用户信息。在后端,我们需要编写Controller来处理用户信息的存储操作,并使用JPA来保存用户数据到数据库中。 登录功能的实现需要验证用户在数据库中是否存在,并验证用户输入的密码和数据库中的密码是否匹配。在Spring Security的配置类中,我们可以设置对登录请求的处理和登录成功后的跳转页面。 总之,Spring BootVue.js的结合可以实现一个简单但完整的登录注册功能,使我们可以快速构建一个轻便可靠的Web应用程序。 ### 回答3: Spring Boot 是一款用于构建微服务的,基于 Spring 框架的开源项目,而 Vue 是一款轻量级的MVVM框架,它能够快速构建前端应用程序。Spring BootVue.js 的结合,可以让我们快速开发高效的全栈应用程序。在实现登录注册的过程中,我们通常会使用 Spring Security 安全组件来实现用户身份验证和授权操作。 在 Spring Boot 后端实现登录注册 1. 导入 Spring Security 相关依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 配置 Spring Security 认证 在 Spring Boot 应用中配置 Spring Security 认证的方案比较简单,只需要在 SecurityConfig 类中对认证策略进行配置即可。下面是一个示例配置,其中包括了登录页面的信息,用户认证方式等。 ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .failureUrl("/login?error=true") .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/"); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } } ``` 3. 配置认证细节 在上一步中,我们指定了一个名为 userDetailsService 的服务,它实现Spring Security 的 UserDetailsService 接口。这个接口定义了查找用户信息和验证用户信息的方法。在此,我们需要自己实现此接口: ``` @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<User> userOptional = userRepository.findByUsername(username); if (!userOptional.isPresent()){ throw new UsernameNotFoundException("Username " + username + " not found"); } User user = userOptional.get(); return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getAuthorities(user)); } private Collection<? extends GrantedAuthority> getAuthorities(User user) { return user.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) .collect(Collectors.toList()); } } ``` 注意,这里还需要在 User 类中添加角色信息,再和数据库交互。上述示例代码中,我们使用了 UserRepository 对象对数据库进行操作,并在 loadUserByUsername() 方法中对用户进行身份验证,并返回一个具有权限的 UserDetails 接口实现。 在 Vue.js 前端实现登录注册 Vue.js 是一款灵活且面向数据的 JavaScript 框架,它提供了一些有用的构建模块,如路由器、状态管理等等。在实现登录注册前端页面时,我们可以利用 Vue.js 提供的基本构建模块,进行快速的应用开发。 1. 安装 Vue.js 在开始使用 Vue.js 之前,我们需要安装 Vue.js 的 npm 包,这可以通过以下命令在终端或命令行中执行: ``` npm install vue ``` 2. 编写前端代码 在编写前端页面代码时,我们可以使用 Vue.js 提供的模板指令,例如 v-model、v-bind 等等来进行页面渲染和操作。下面是一个简单的登录页面示例: ``` <template> <div> <h1>Login Form</h1> <form> <div> <label for="username">Username:</label> <input type="text" id="username" v-model="username"> </div> <div> <label for="password">Password:</label> <input type="password" id="password" v-model="password"> </div> <button @click="login()">Login</button> </form> </div> </template> <script> export default { data() { return { username: '', password: '' } }, methods: { login() { // 在这里实现前端登录验证逻辑 } } } </script> ``` 在这个代码段中,我们通过 v-model 指令来绑定了表单输入域的值,然后在 methods 中定义了一个 login() 方法,用于在点击登录按钮时进行登录验证操作。我们可以通过调用后台 Spring Boot API 来实现此操作。 3. 实现后端交互 在前后端交互方面,我们可以使用 axios 库来进行异步请求操作。下面是一个 Vue.js 调用后台登录接口的示例: ``` <script> import axios from 'axios' export default { data() { return { username: '', password: '' } }, methods: { login() { axios.post('/api/login', {username: this.username, password: this.password}) .then(response => { if(response.data.status == 'success') { alert('登录成功!'); } else { alert('登录失败,请检查用户名或密码!'); } }) .catch(error => { console.error(error); }) } } } </script> ``` 在这个代码段中,我们使用了 Vue.js 插件 axios,调用了一个后台登录接口。在该接口中,我们传入了用户名和密码,并在登录成功后发回一个 success 消息。这里需要注意,登录接口的路径和主机地址需要根据实际情况进行调整。 最后,我们需要将前端和后端代码整合到一起,并进行测试,以确保登录注册功能正常运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值