登录功能描述:

前端输入对应的用户信息 ,在用户输入邮箱后 点击发送按钮给邮箱发送验证码,拿取到验证填完之后,点击登录按钮给后端发送请求 ,后端接收到请求之后 ,第一步校验验证码,第二步校验用户名和密码是否正确,通过验证之后,表示登录成功,再给前端响应数据(之前返回的是登录用户,现在使用的是token,它是给返回的对象进行加密),  前端拿到后端响应的数据token, ,再次给后端发送请求,查询该用户的角色, 前端拿取到用户的角色之后,根据角色的不同跳转到不同的页面;

 

 前端页面:

1.前端基本 登录页面

 

<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">

      <div class="title-container">
        <h3 class="title">登录</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="Username"
          name="username"
          type="text"
          tabindex="1"
          autocomplete="on"
        />
      </el-form-item>

      <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
        <el-form-item prop="password">
          <span class="svg-container">
            <svg-icon icon-class="password" />
          </span>
          <el-input
            :key="passwordType"
            ref="password"
            v-model="loginForm.password"
            :type="passwordType"
            placeholder="Password"
            name="password"
            tabindex="2"
            autocomplete="on"
            @keyup.native="checkCapslock"
            @blur="capsTooltip = false"
            @keyup.enter.native="handleLogin"
          />
          <span class="show-pwd" @click="showPwd">
            <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
          </span>
        </el-form-item>
      </el-tooltip>

      <el-form-item prop="email">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="email"
          v-model="loginForm.email"
          placeholder="请输入邮箱"
          name="email"
          type="text"
          tabindex="3"
          style="width: 70%;"
          autocomplete="on"
        />
        <el-button  type="primary" style="width:20%;" @click.native.prevent="sendEmail">发送</el-button>
      </el-form-item>

      <el-form-item prop="verifycode">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="verifycode"
          v-model="loginForm.verifycode"
          placeholder="请输入验证码"
          name="verifycode"
          type="text"
          tabindex="4"
          autocomplete="on"
        />
      </el-form-item>

      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
      <el-button type="primary" style="width:100%;margin-bottom:30px;margin-left: 0px;" @click="$router.push('/register')">注册</el-button>

      <div style="position:relative">
        <div class="tips">
          <span>Username : admin</span>
          <span>Password : any</span>
        </div>
        <div class="tips">
          <span style="margin-right:18px;">Username : editor</span>
          <span>Password : any</span>
        </div>

        <el-button class="thirdparty-button" type="primary" @click="showDialog=true">
          Or connect with
        </el-button>
      </div>
    </el-form>

    <el-dialog title="Or connect with" :visible.sync="showDialog">
      Can not be simulated on local, so please combine you own business simulation! ! !
      <br>
      <br>
      <br>
      <social-sign />
    </el-dialog>
  </div>
</template>

<script>
import { validUsername } from '@/utils/validate'
import {sendEmail} from '@/api/user'
import SocialSign from './components/SocialSignin'

export default {
  name: 'Login',
  components: { SocialSign },
  data() {
    const validateUsername = (rule, value, callback) => {
      if (!validUsername(value)) {
        callback(new Error('Please enter the correct user name'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 6) {
        callback(new Error('The password can not be less than 6 digits'))
      } else {
        callback()
      }
    }
    return {
      loginForm: {
        username: 'admin',
        password: '111111',
        email:'wangshan20010907@126.com'
      },
      loginRules: {
        // username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        // password: [{ required: true, trigger: 'blur', validator: validatePassword }]
      },
      passwordType: 'password',
      capsTooltip: false,
      loading: false,
      showDialog: false,
      redirect: undefined,
      otherQuery: {}
    }
  },
  watch: {
    $route: {
      handler: function(route) {
        const query = route.query
        if (query) {
          this.redirect = query.redirect
          this.otherQuery = this.getOtherQuery(query)
        }
      },
      immediate: true
    }
  },
  created() {
    // window.addEventListener('storage', this.afterQRScan)
  },
  mounted() {
    if (this.loginForm.username === '') {
      this.$refs.username.focus()
    } else if (this.loginForm.password === '') {
      this.$refs.password.focus()
    }
  },
  destroyed() {
    // window.removeEventListener('storage', this.afterQRScan)
  },
  methods: {
    //发送邮箱
    async sendEmail(){
      let {message}=await sendEmail(this.loginForm);
      this.$message.success(message);
    
    },

    checkCapslock(e) {
      const { key } = e
      this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
    },
    showPwd() {
      if (this.passwordType === 'password') {
        this.passwordType = ''
      } else {
        this.passwordType = 'password'
      }
      this.$nextTick(() => {
        this.$refs.password.focus()
      })
    },
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/login', this.loginForm)
            .then((data) => {
              this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
              this.loading = false
            })
            .catch((error) => {
              console.info('@/views/login:165',error)
              this.loading = false
            })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    getOtherQuery(query) {
      return Object.keys(query).reduce((acc, cur) => {
        if (cur !== 'redirect') {
          acc[cur] = query[cur]
        }
        return acc
      }, {})
    }
    // afterQRScan() {
    //   if (e.key === 'x-admin-oauth-code') {
    //     const code = getQueryObject(e.newValue)
    //     const codeMap = {
    //       wechat: 'code',
    //       tencent: 'code'
    //     }
    //     const type = codeMap[this.auth_type]
    //     const codeName = code[type]
    //     if (codeName) {
    //       this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
    //         this.$router.push({ path: this.redirect || '/' })
    //       })
    //     } else {
    //       alert('第三方登录失败')
    //     }
    //   }
    // }
  }
}
</script>

<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */

$bg:#283443;
$light_gray:#fff;
$cursor: #fff;

@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  .login-container .el-input input {
    color: $cursor;
  }
}

/* reset element-ui css */
.login-container {
  .el-input {
    display: inline-block;
    height: 47px;
    width: 85%;

    input {
      background: transparent;
      border: 0px;
      -webkit-appearance: none;
      border-radius: 0px;
      padding: 12px 5px 12px 15px;
      color: $light_gray;
      height: 47px;
      caret-color: $cursor;

      &:-webkit-autofill {
        box-shadow: 0 0 0px 1000px $bg inset !important;
        -webkit-text-fill-color: $cursor !important;
      }
    }
  }

  .el-form-item {
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    color: #454545;
  }
}
</style>

<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;

.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;
  }

  .tips {
    font-size: 14px;
    color: #fff;
    margin-bottom: 10px;

    span {
      &:first-of-type {
        margin-right: 16px;
      }
    }
  }

  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    width: 30px;
    display: inline-block;
  }

  .title-container {
    position: relative;

    .title {
      font-size: 26px;
      color: $light_gray;
      margin: 0px auto 40px auto;
      text-align: center;
      font-weight: bold;
    }
  }

  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }

  .thirdparty-button {
    position: absolute;
    right: 0;
    bottom: 6px;
  }

  @media only screen and (max-width: 470px) {
    .thirdparty-button {
      display: none;
    }
  }
}
</style>

2.登录页面的路由配置

 3.修改框架模拟的路径  换成我们自己的后端逻辑

 

 vue 可以自动生成这些代码 .注意的地方都标注出来了

 

后端:

 

1.子类pom文件依赖


    <dependencies>
        <!--web起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>


        <!-- nacos 服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>

        <!-- feign 远程调用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis.plus.version}</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--自定义项目-->
        <dependency>
            <groupId>com.czxy</groupId>
            <artifactId>zx_common01</artifactId>
        </dependency>
        <dependency>
            <groupId>com.czxy</groupId>
            <artifactId>zx_domain01</artifactId>
        </dependency>

        <!-- redis 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- JavaMail 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <!-- MQ 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <!--JavaBean工具类,用于JavaBean数据封装-->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
        </dependency>
        <!--jwt工具-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

        <!--joda 时间工具类 -->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
    </dependencies>

application.yml

# 服务端口号
server:
  port: 9010
# 服务名
spring:
  application:
    name: user-service
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/zx_edu_user?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
    druid:    #druid 连接池配置
      initial-size: 1       #初始化连接池大小
      min-idle: 1           #最小连接数
      max-active: 20        #最大连接数
      test-on-borrow: true  #获取连接时候验证,会影响性能
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848   #nacos服务地址
  redis:
    database: 0       #数据库索引,取值0-15,表示16个库可选择
    host: 127.0.0.1   #服务器地址
    port: 6379        #服务器连接端口号
  mail:
    host: smtp.163.com          #发送邮件服务器
    username: wangshan20010907@163.com #账号
    password: IBCEGYGFTZMQFFQY          #授权密码
    default-encoding: UTF-8     #默认编码时
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    passowrd: guest
    virtualHost: /

  freemarker:
    cache: false    #页面不加载缓存,修改即时生效

#开启log4j打印SQL语句
logging:
  level:
    com:
      czxy:
        zx:
          user:
            mapper: debug

# mp日志打印
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0

sc:
  jwt:
    secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥
    pubKeyPath: D:/rsa/rsa.pub # 公钥地址
    priKeyPath: D:/rsa/rsa.pri # 私钥地址
    expire: 360 # 过期时间,单位分钟





3.配置类utils:

验证码VerifyCodeUtils :

package com.czxy.zx.utils;

import com.czxy.zx.vo.VerifyCode;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

public class VerifyCodeUtils {

    //获取验证码
    public static VerifyCode getVerifyCode(){
        VerifyCode verifyCode = new VerifyCode();

        //字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
        String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";

        int IMG_WIDTH = 72;
        int IMG_HEIGTH = 27;
        Random random = new Random();
        //创建图片
        BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGTH, BufferedImage.TYPE_INT_RGB);
        //画板
        Graphics g = image.getGraphics();
        //填充背景
        g.setColor(Color.WHITE);
        g.fillRect(1,1,IMG_WIDTH-2,IMG_HEIGTH-2);

        g.setFont(new Font("楷体",Font.BOLD,25));

        StringBuilder sb = new StringBuilder();
        //写字
        for(int i = 1 ; i <= 4 ; i ++){
            //随机颜色
            g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
            int len = random.nextInt(VERIFY_CODES.length());
            String str = VERIFY_CODES.substring(len,len+1);
            sb.append(str);
            g.drawString(str, IMG_WIDTH / 6 * i , 22 );
        }

        // 生成随机干扰线
        for (int i = 0; i < 30; i++) {
            //随机颜色
            g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
            int x = random.nextInt(IMG_WIDTH - 1);
            int y = random.nextInt(IMG_HEIGTH - 1);
            int x1 = random.nextInt(12) + 1;
            int y1 = random.nextInt(6) + 1;
            g.drawLine(x, y, x - x1, y - y1);
        }

        verifyCode.setCode(sb.toString());
        verifyCode.setImage(image);
        verifyCode.setType("jpeg");

        return verifyCode;
    }

}

JwtUtils类:

package com.czxy.zx.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.DateTime;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Created by wangshan.
 */
public class JwtUtils {
    /**
     *  私钥加密token
     * @param data 需要加密的数据(载荷内容)
     * @param expireMinutes 过期时间,单位:分钟
     * @param privateKey 私钥
     * @return
     */
    public static String generateToken(Object data, int expireMinutes, PrivateKey privateKey)  {
        try {
            //1 获得jwt构建对象
            JwtBuilder jwtBuilder = Jwts.builder();
            //2 设置数据
            if( data == null ) {
                throw new RuntimeException("数据不能为空");
            }
            BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获得属性名
                String name = propertyDescriptor.getName();
                // 获得属性值
                Object value = propertyDescriptor.getReadMethod().invoke(data);
                if(value != null) {
                    jwtBuilder.claim(name,value);
                }
            }
            //3 设置过期时间
            jwtBuilder.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate());
            //4 设置加密
            jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey);
            //5 构建
            return jwtBuilder.compact();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过公钥解析token
     * @param token 需要解析的数据
     * @param publicKey 公钥
     * @param beanClass 封装的JavaBean
     * @return
     * @throws Exception
     */
    public static <T> T  getObjectFromToken(String token, PublicKey publicKey,Class<T> beanClass) throws Exception {
        //1 获得解析后内容
        Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
        //2 将内容封装到对象JavaBean
        T bean = beanClass.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 获得属性名
            String name = propertyDescriptor.getName();
            // 通过属性名,获得对应解析的数据
            Object value = body.get(name);
            if(value != null) {
                // 将获得的数据封装到对应的JavaBean中
                BeanUtils.setProperty(bean,name,value);
            }
        }
        return bean;
    }
}
RsaUtils类
package com.czxy.zx.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Created by wangshan.
 */
public class RsaUtils {

    /**
     * 从文件中读取公钥
     *
     * @param filename 公钥保存路径,相对于classpath
     * @return 公钥对象
     * @throws Exception
     */
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 从文件中读取密钥
     *
     * @param filename 私钥保存路径,相对于classpath
     * @return 私钥对象
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 获取公钥
     *
     * @param bytes 公钥的字节形式
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    /**
     * 获取密钥
     *
     * @param bytes 私钥的字节形式
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    /**
     * 根据密文,生存rsa公钥和私钥,并写入指定文件
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @throws Exception
     */
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);

        //创建父文件夹
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdirs();
        }
        //创建需要的文件
        if (!dest.exists()) {
            dest.createNewFile();
        }

        Files.write(dest.toPath(), bytes);
    }


}

3.配置类config:

JwtProperties :

package com.czxy.zx.user.config;


import com.czxy.zx.utils.RsaUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;


@Configuration
@ConfigurationProperties(prefix = "sc.jwt")
@Data
public class JwtProperties {
    private String secret;
    private String pubKeyPath;
    private String priKeyPath;
    private Integer expire;
    private PublicKey publicKey;
    private PrivateKey privateKey;

    @PostConstruct      //初始化方法注解
    public void init() {
        try {
            File pubFile = new File(pubKeyPath);
            File priFile = new File(priKeyPath);
            if(!pubFile.exists() || ! priFile.exists()) {
                RsaUtils.generateKey(pubKeyPath,priKeyPath,secret);
            }
            // 获得公钥和私钥对象
            this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
            this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 MyBatisPlusConfig :

package com.czxy.zx.user.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

免前端页面测试:

Swagger2ConfigurationV3 

package com.czxy.zx.user.config;

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.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

/**
 * Swagger2 配置类,
 * 访问路径:swagger-ui.html
 * 自动注册:
 *     位置:resources/META-INF/spring.factories
 *     内容:
 *        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 *          com.czxy.changgou4.config.Swagger2Configuration
 */
@Configuration
@EnableSwagger2
public class Swagger2ConfigurationV3 {

    @Bean
    public Docket createRestApi() {
        // 1 确定文档Swagger版本
        Docket docket = new Docket(DocumentationType.SWAGGER_2);
        // 2 设置 api基本信息
        docket.apiInfo(apiInfo());
        // 3 设置自定义加载路径
        docket = docket.select()
                .apis(RequestHandlerSelectors.basePackage("com.czxy"))
                .paths(PathSelectors.any())
                .build();
        //4 设置权限
        docket.securitySchemes(securitySchemes());
        docket.securityContexts(securityContexts());

        return docket;
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("API")
                .description("基于swagger接口文档")
                .contact(new Contact("java34","http://www.javaliang.com","liangtong@itcast.cn"))
                .version("1.0")
                .build();
    }

    private List<ApiKey> securitySchemes() {
        List<ApiKey> list = new ArrayList<>();
        // name 为参数名  keyname是页面传值显示的 keyname, name在swagger鉴权中使用
        list.add(new ApiKey("Authorization", "Authorization", "header"));
        return list;
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> list = new ArrayList<>();
        list.add(SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex("^(?!auth).*$"))
                .build());
        return list;
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> list = new ArrayList();
        list.add(new SecurityReference("Authorization", authorizationScopes));
        return list;
    }

}

队列的配置类:

RabbitEmailConfig 

package com.czxy.zx.user.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;


@Component
public class RabbitEmailConfig {

    //队列名称
    public static final String QUEUE_LOGIN_EMAIL="login_email";
    public static final String QUEUE_REGISTER_EMAIL="register_email";

    //生成队列对象
   @Bean(QUEUE_LOGIN_EMAIL)
    public Queue queue(){
        return new Queue(QUEUE_LOGIN_EMAIL);
    }

    //生成队列对象
    @Bean(QUEUE_REGISTER_EMAIL)
    public Queue queue2(){
        return new Queue(QUEUE_REGISTER_EMAIL);
    }
}

登录service:

package com.czxy.zx.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.exception.EduException;
import com.czxy.zx.user.mapper.EduUserMapper;
import com.czxy.zx.user.service.EduUserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

@Service
@Transactional
public class EduUserServiceImpl extends ServiceImpl<EduUserMapper, EduUser> implements EduUserService {
    @Override
    public boolean register(EduUser user) {
        QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",user.getUsername());
        EduUser selectOne = baseMapper.selectOne(queryWrapper);
        if(selectOne!=null){
            //用户已注册
            throw  new EduException("该用户名已经存在啦");
        }
        user.setStatus("0");
        user.setCreated(new Date());
        int insert = baseMapper.insert(user);
        return insert==1;
    }

    @Override
    public EduUser login(EduUser user) {

        QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(user.getUsername()),"username",user.getUsername());
        queryWrapper.eq(StringUtils.isNotBlank(user.getPassword()),"password",user.getPassword());

        List<EduUser> list = baseMapper.selectList(queryWrapper);

        if(list.size()>0){
            //在数据找到
            return list.get(0);
        }
        return null;
    }
}

登录controller层:

package com.czxy.zx.user.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.czxy.zx.domain.EduUser;
import com.czxy.zx.user.config.JwtProperties;
import com.czxy.zx.user.config.RabbitEmailConfig;
import com.czxy.zx.user.service.EduUserService;
import com.czxy.zx.utils.JwtUtils;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vo.UserEmail;
import com.google.gson.JsonObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
public class EduUserController {

    //redis相关实体类
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //mq消息队列的实体类
    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private EduUserService eduUserService;

    @Resource
    private JwtProperties jwtProperties;

    @GetMapping("/info")
    public BaseResult getInfo(String token) {
        System.out.println("info-->"+token);
        Map<String,Object> map = new HashMap<>();
        try {
            //使用jwt工具解析token得到用户, 获取用户的角色
            EduUser user = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class);
            System.out.println("user = "+user);
            if(user.getRoles()!=null){
                map.put("roles", user.getRoles().split(","));
            }else {
                //为普通用户
                map.put("roles",Arrays.asList("editor"));
            }
            map.put("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
            map.put("name",user.getUsername());


        } catch (Exception e) {
            e.printStackTrace();
            return BaseResult.error("获取用户角色失败");
        }

        System.out.println(map);

        return BaseResult.ok("登录成功",map);
    }

    @GetMapping("/logOut")
    public BaseResult logOut(){
        return BaseResult.ok("退出成功啦~");
    }


    @GetMapping("/active")
    public BaseResult active(String uuid){
        //1.从redis中获取该uuid 对应的用户名username,并从redis中删除
        String username = stringRedisTemplate.opsForValue().get(uuid);
        //2.校验
        // username==null
        if(username==null){
            return BaseResult.error("超出激活时间, 或该用户已经成功激活");
        }
        //username如果存在 则去数据库中根据用户名获取该用户信息
        QueryWrapper<EduUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",username);
        EduUser one = eduUserService.getOne(queryWrapper);

        //判断如果不存在该用户 则给出提示
        if(one==null){
            return BaseResult.error("该用户不存在");
        }
        //否则判断该用户是否要激活 1(激活过) 0(未激活)
        if(!"0".equalsIgnoreCase(one.getStatus())){
            return BaseResult.error("该用户已经激活过,无需再次激活");
        }

        //修改状态
        one.setStatus("1");
        eduUserService.updateById(one);
        stringRedisTemplate.delete(uuid);
        return BaseResult.ok("修改成功");

    }

    @PostMapping("/register")
    public BaseResult register(@RequestBody EduUser user){

        //先校验验证码是否正确
        String vsCode = stringRedisTemplate.opsForValue().get("register_" + user.getUsername());
        stringRedisTemplate.delete("register_"+user.getUsername());

        if(vsCode==null){
            return BaseResult.error("验证码已经失效l ");
        }

        if(!vsCode.equalsIgnoreCase(user.getVerifycode())){
            return BaseResult.error("验证码错误");
        }

        boolean rel = eduUserService.register(user);
        if(rel){
            //成功 则随机生成一个uuid 并发送邮箱 需要用户激活
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            stringRedisTemplate.opsForValue().set(uuid,user.getUsername(),50, TimeUnit.MINUTES);
//            String url="http://localhost:19528/#/active?uuid="+uuid;
            String url="<a href='http://localhost:19528/#/active?uuid="+uuid+"'>点击激活<a/>";
            String text=user.getUsername()+" 你好,您在使用本站的激活程序,请点击 "+url+" 进行激活";
            //拼装路径
            UserEmail userEmail = new UserEmail();
            userEmail.setUsername(user.getUsername());
            userEmail.setText(text);
            userEmail.setEmail(user.getEmail());

            //存放在消息队列中
            String s = JSONObject.toJSONString(userEmail);
            rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_REGISTER_EMAIL,s);

            return BaseResult.ok("注册成功,去登录");
        }else {
            return BaseResult.error("注册失败啦,");
        }
    }

    @PostMapping("/login")
    public BaseResult login(@RequestBody EduUser user){
        //1.校验验证码
        //获取验证码
        String vsCode = stringRedisTemplate.opsForValue().get("login_" + user.getUsername());
        // 删除redis中的一次性验证码
        stringRedisTemplate.delete("login_"+user.getUsername());

        if(vsCode==null){
            return BaseResult.error("验证码失效了");
        }
        if (!user.getVerifycode().equalsIgnoreCase(vsCode)){
            return BaseResult.error("验证码错误");
        }

        //校验用户名和密码是否正确
        EduUser loginU = eduUserService.login(user);
        if(loginU!=null){
           //String token="admin-token";

            EduUser lu = new EduUser();//把当前用户非敏感信息作为token发送给前端
            lu.setRoles(loginU.getRoles());
            lu.setUsername(loginU.getUsername());

            String token = JwtUtils.generateToken(lu, jwtProperties.getExpire(), jwtProperties.getPrivateKey());
            System.out.println("token= "+token);
            return BaseResult.ok("登录成功").append("token",token);
        }else {
            return BaseResult.error("用户名或者密码错误,登录失败");
        }
    }

    @PostMapping("/sendEmail")
    public BaseResult sendEmail(@RequestBody EduUser user){

        //生成四位数的验证码
        int number = new Random().nextInt(8999) + 1000;

        //存入redis数据库中
        stringRedisTemplate.opsForValue().set("login_"+user.getUsername(),number+"");

        UserEmail userEmail = new UserEmail();
        userEmail.setUsername(user.getUsername());
        userEmail.setEmail(user.getEmail());
        userEmail.setText("登录密码是:"+number);

        //把验证码发送到消息队列中
        String s = JSONObject.toJSONString(userEmail);
        rabbitTemplate.convertAndSend(RabbitEmailConfig.QUEUE_LOGIN_EMAIL,s);

        return BaseResult.ok("邮件发送中,请注意查阅");


    }

}
package com.czxy.zx.user.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 EduHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("created",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("created",new Date(),metaObject);
    }
}

验证码controller层:

package com.czxy.zx.user.controller;

import com.czxy.zx.utils.VerifyCodeUtils;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vo.VerifyCode;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@RequestMapping("/verifyCode")
public class VerifyCodeController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @GetMapping("/{username}")
    public void getVerifyCode(@PathVariable("username") String username, HttpServletResponse response) throws IOException {

        VerifyCode verifyCode = VerifyCodeUtils.getVerifyCode();

        stringRedisTemplate.opsForValue().set("register_"+username,verifyCode.getCode());
        //给前端响应图片
        ImageIO.write(verifyCode.getImage(),verifyCode.getType(),response.getOutputStream());
    }

}

EduUser

package com.czxy.zx.domain;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import lombok.Data;

import java.util.Date;

/**
 *
 */
@Data
@TableName("edu_user")
@ApiModel(value = "EduUser对象",description = "用户")
public class EduUser {

    @TableId(value="id" , type = IdType.AUTO)
    private Integer id;
    //用户名
    private String username;
    //用户密码
    private String password;
    //电话
    private String phone;
    //邮箱
    private String email;
    //角色,多个值使用逗号分隔,例如:admin,editor
    private String roles;
    //创建时间
    @TableField(value = "created",fill = FieldFill.INSERT)
    private Date created;
    //状态:0 未激活、1已激活
    private String status;

    @TableField(exist = false)
    private String verifycode;

    @TableField(exist = false)
    private String repassword;

}

Logo

前往低代码交流专区

更多推荐