前端端分离实现含验证码的登录

前端端分离实现含验证码的登录,第1张

提示:

前后端分离实现含验证码登录 前言一、先来看看页面成果二、前端开发Login代码: 三.后端开发pom.xmlapplication.ymlconfig类:interceptions类utils 类pojo层mapper层service层controller层 总结

前言

花了一下午,终于做出能够使用验证码的登录页面。
因为前后端不分离的情况下,验证码是存储在session中的,然后调用需要用到验证码功能的接口时,从session中获取验证码的值,然后与前端页面传过来的值进行比对。但前后端分离用不到session,所以
基于前后端分离的基础上,我做了含验证码的登录功能,后端验证码使用redis存储,解决验证码存储的问题。具体实现流程,让我们往下看吧。


一、先来看看页面成果

二、前端开发
因为是学后端的,前端不是特别熟,如果有什么错误或不好的地方,欢迎大家指出来。

	路由跳转之类的都是基础配置,就不
   一一展示代码了,直接上登录页面吧
Login代码:
<template>
  <div id="background">
   <div class="login-box">
    <div class="login-left">
      <div class="text-left">
       <h2>Welocme</h2>
       <h2>进入外卖系统后台</h2>
      </div> 
   </div>
   <div class="login-right">
    <h2 class="login-title"><el-icon><platform/></el-icon> 后台登录系统</h2>
    <el-form :model="form" :rules="rules" label-width="80px">
      <el-form-item label="用户名" prop="username">
        <el-input :prefix-icon="User" :suffix-icon="ArrowDown" v-model.trim="form.username" placeholder="请输入用户名"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input
          :suffix-icon="Hide"
          :prefix-icon="Unlock"
          type="password"
          v-model.trim="form.password"
          placeholder="请输入密码"
        ></el-input>
      </el-form-item>
     <el-form-item label="验证码" prop="verifyCode">
       <el-input v-model.trim="form.verifyCode" :prefix-icon="EditPen" class="code" size="large" placeholder="请输入验证码"/>
            <img src="http://localhost:8080/kaptcha" alt="" class="codeimg">
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="login()">登录</el-button>
        <el-button @click="resetForm(form)">重置</el-button>
      </el-form-item>
    </el-form>
   </div>
   </div>
  </div> 
</template>



<script setup>
import { reactive,ref } from 'vue'
import axios from "axios"
import { Hide, Unlock,User,ArrowDown,Platform,EditPen } from '@element-plus/icons-vue'
import router from '../../router/index'



const form = reactive({
  username: 'Jone',
  password: '12345',
  verifyCode: ''
})  
const rules = reactive({
   username: [
    { required: true, message: '请输入用户名!', trigger: 'blur' },
    { min: 3, max: 7, message: '用户名长度为5-12', trigger: 'blur' },
    ],
    password: [
      { required: true, message: '请输入密码!', trigger: 'blur' },
      { min: 5, max: 12, message: '密码长度为5-12', trigger: 'blur' },
    ],
    verifyCode: [
      { required: true, message: '请输入验证码!', trigger: 'blur' },
    ],
})
function login() {
    axios({
        method: 'post',
        url: 'http://localhost:8080/customer/login',
        data:{
           username: form.username,
           password: form.password,
           verifyCode: form.verifyCode
        },
      }).then(resp =>{
        if(resp.data.flag==true){
          //登录成功
          // userInfo.setToken(resp.data.data);
          router.push("/") //跳转路由
        }else{
         console.log('用户名或密码错误');
          return false;
        }
      });
  }


function resetForm(form){
  form.username = "",
  form.password = ""
} 

</script>

<style scoped>
#background {
  width: 100%;
  height: 100%;
  background: url("../../assets/50.jpg");
  background-size: 100% 100%;
  position: fixed;
  top: 0;
  left: 0;
}
.login-box {
  height: 350px;
  width: 800px;
  margin: 250px auto; 
  border: 1px solid black;
  position: relative;
}
.login-left{
 height: 100%;
 width: 400px; 
 position:absolute;
 text-align: center;
 text-align: center;
}
.login-right{
 height: 100%;
 width: 400px; 
 margin-left: 400px;
 position:absolute;
 background-color: white ;
}
.login-title {
  text-align: center;
  margin-bottom: 30px;
}
.text-left{
  padding-top: 100px;
}
.el-form-item{
  padding: 8px;
}
.regist{
  padding-left: 320px;
  color: red;
}
.el-button{
    margin: 0 50px 0 33px;
}
.el-icon{
    vertical-align: middle
}
.code{
  width: 120px;
}
.codeimg{
  padding-left: 50px;
}
</style>

三.后端开发 pom.xml

要导入的依赖

 <!--mybatisPlus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--druid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!--mysql数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--JWT依赖-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.15.0</version>
        </dependency>
        <!-- kaptcha验证码 -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        <!--Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
application.yml

redis记得写上你自己的host

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3307/xinxin_takeout?characterEncoding=utf-8&userSSL=false
      username: root
      password: root
  redis:
    host: **********
    port: 6379
    database: 0
    timeout: 1800000
    lettuce:
      pool:
        max-active: 20
        max-wait: -1
        max-idle: 5
        min-idle: 0
mybatis-plus:
  global-config:
    db-config:
      id-type: auto
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

config类:
JTW的拦截器配置以及验证码的相关配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/books/**")          //其他接口token验证
                .excludePathPatterns("/user/login")     //放行
                .excludePathPatterns("/user/register");   //放行
    }

}
@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha getDefaultKaptcha(){
        DefaultKaptcha captchaProducer = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "yes");
        properties.setProperty("kaptcha.border.color", "105,179,90");
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        properties.setProperty("kaptcha.image.width", "110");
        properties.setProperty("kaptcha.image.height", "40");
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        properties.setProperty("kaptcha.session.key", "code");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        Config config = new Config(properties);
        captchaProducer.setConfig(config);
        return captchaProducer;

    }
}
interceptions类

JWT拦截器的编写,网上都是有代码的,直接复制下来就行

public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if("OPTIONS".equals(request.getMethod().toUpperCase())) {
            System.out.println("Method:OPTIONS");
            return true;
        }
        Map<String,Object> map = new HashMap<>();
        //获取请求头中的令牌
        String token = request.getHeader("Token");
        try{
            //验证令牌
            JWTUtils.verify(token);
            return true;//放行请求
        }catch (JWTDecodeException e){              //捕捉签名异常怎么办
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){                       //捕捉令牌过期异常
            e.printStackTrace();
            map.put("msg","令牌过期");
        }catch (AlgorithmMismatchException e){                  //捕捉算法不匹配异常
            e.printStackTrace();
            map.put("msg","算法不匹配");
        }catch (InvalidClaimException e){                       //捕捉失效的payload异常
            e.printStackTrace();
            map.put("msg","失效的payload ");
        }catch (Exception e){
            map.put("msg","其他的异常");
            e.printStackTrace();
        }
        map.put("state",false); //设置状态
        //将map转为JSON jackson 发给前端
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}
utils 类

统一响应

import lombok.Data;

@Data
public class R<T> {

    private boolean flag;
    private T data;
    private String msg;


    public R(boolean flag, T data, String msg) {
        this.flag = flag;
        this.data = data;
        this.msg = msg;
    }

    public R(boolean flag, T data) {
        this.flag = flag;
        this.data = data;
    }

    public R(boolean flag) {
        this.flag = flag;
    }

}

JWT工具类

public class JWTUtils {

    //签名
    private static final String SIGN = "!QQ546R@FA$asf564E##S45$%456asfdFGAag*^SDg#%FG";
    /**
     * 生成token  header.payload.signature
     */
    public static String getToken(Map<String,String> map) {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.HOUR,1);      //过期时间

        //创建jwt builder
        JWTCreator.Builder builder = JWT.create();

        //payload(传入的map数据)
        map.forEach((k,v) ->{
            builder.withClaim(k,v);
        });
        String token = builder.withExpiresAt(instance.getTime()) //指定过期时间
                .sign(Algorithm.HMAC256(SIGN));//sign
        return token;
    }

    /**
     *  验证token合法性
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }

    /**
     * 获取token信息
     */
    public static DecodedJWT getTokenInfo(String token){
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        return verify;
    }
}
pojo层

都是mybatisplus自动生成的

@TableName(value ="u_customer")
@Data
public class Customer implements Serializable {
    @TableId(type = IdType.AUTO)
    private Long cId;
    private Long username;
    private String password;
}

mapper层
@Mapper
public interface CustomerMapper extends BaseMapper<Customer> {
    Customer login(@Param("username") String username);
}
<select id="login" resultType="com.xinxue.pojo.Customer">
        select * from u_customer where username=#{username}
</select>
service层
public interface CustomerService extends IService<Customer> {

    R<String> login(String username, String password,String verifyCode);
}


/**
* @author zhang
* @description 针对表【u_customer】的数据库 *** 作Service实现
* @createDate 2022-05-09 22:04:27
*/
@Service
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements CustomerService{

    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public R<String> login(String username, String password,String verifyCode) {
        Map<String,String> map = new HashMap<String,String>();
        String capText = (String) redisTemplate.opsForValue().get("capText");
        System.out.println(capText);

        Customer customer = customerMapper.login(username);
        if (customer!=null){
            password = DigestUtils.md5DigestAsHex(password.getBytes());
            if (password.equals(customer.getPassword()) && verifyCode.equals(capText)){
                map.put("name",customer.getName());
                map.put("sex",customer.getSex());
                map.put("age",String.valueOf(customer.getAge()));
                map.put("email",customer.getEmail());
                map.put("phone",customer.getPhone());
                String token = JWTUtils.getToken(map);
                return new R<String>(true,token,"登录成功!!!");
            }
        }
        return new R<String>(false,null,"用户名或密码错误!!!");
    }
}

controller层
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;

@Controller
public class KaptchaController {

    @Autowired
    private Producer captchaProducer ;

    @Autowired
    private RedisTemplate redisTemplate;

    @CrossOrigin
    @RequestMapping("/kaptcha")
    public void getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpSession session = request.getSession();
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");
        //生成验证码
        String capText = captchaProducer.createText();
        redisTemplate.opsForValue().set("capText",capText);
        //向客户端写出
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }


}
@RestController
@RequestMapping("/customer")
public class CustomerController {

    @Autowired
    private CustomerService customerService;
//    登录
    @PostMapping("/login")
    @CrossOrigin
    public R<String> login(@RequestBody Map<String,String> map){
        String username =  map.get("username");
        String password =  map.get("password");
        String verifyCode =  map.get("verifyCode");
        return customerService.login(username, password, verifyCode);
    }
 }   

总结

因为第一次发csdn,还是有很多不足的地方,我感觉写进去的东西太多了,有很多东西其实不用写上去的,就比如mybatisplus自动生成的那些,但是为了代码完整写,还是附带上去了,还有就是JWT和验证码的代码,都是自己网上找的,也是没有太多的必要,但不加上又感觉哪里少了点东西。希望大家能多给一点鼓励。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/946426.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-18
下一篇 2022-05-18

发表评论

登录后才能评论

评论列表(0条)

保存