uniapp中使用JWT

关于JWT的原理,这里不细说。以一个例子来阐述在uniapp中使用JWT从后端到前端的过程,代码仅用于帮助说明问题,所以只列出其中核心部分。这里假定对SpringBoot有些了解。

1. 创建表及相应的mapper,service,controller等类。

​在MySQL中创建user表:

字段数据类型说明
IdInt(11)主键,自动增长
usernamevarchar(50)账号
passwordvarchar(50)密码
emailvarchar(50)邮箱
create_timedatetime创建时间
modify_timedatetime修改时间

​实体类:User.java,放在pojo包下面。

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "Id", type = IdType.AUTO)
    private Long id;

    private String username;

    private String password;

    private String email;

    private LocalDateTime createTime;

    private LocalDateTime modifyTime;

}

mapper类:UserMapper.java,放在mapper包下面。

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {

}

UserMapper.xml文件:放在resources/mybatis文件夹下面。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.edu.hbue.mapper.UserMapper">

</mapper>

业务层接口:IUserService.java,放在service包下面。

import com.baomidou.mybatisplus.extension.service.IService;

public interface IUserService extends IService<User> {

}

业务实现类UserServiceImple.java, 放在service/impl包下面。

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private UserMapper userMapper;
}

控制层:UserController.java,放在controller包下面。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;
    
}

创建dto包,在该包下面创建 LoginDTO.java:

import lombok.Data;

@Data
public class LoginDTO {

    private String username;

    private String password;

}
2. 后端生成token

​在pom.xml中加入相关的依赖:

 <!-- jwt -->
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>3.2.2</version>
</dependency>

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.76</version>
</dependency>

​在util包下面创建JWTUtils.java文件,代码如下:

import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//基于jsonwebtoken包生成token和解析token的工具类比较多
//此处代码参考up主mszl
public class JWTUtils {

    private static final String jwtToken = "1234567890!@#$$";

    public static String createToken(Long userId){
        Map<String,Object> claims = new HashMap<>();
        claims.put("userId",userId);
        JwtBuilder jwtBuiler = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,jwtToken)
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis()+24*60*60*1000));
        String token = jwtBuiler.compact();
        return token;
    }

    public static Map<String, Object> checkToken(String token){
        try {
            Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
            return (Map<String,Object>)parse.getBody();
        }catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

}

​给IUserService增加一个方法:

R login(LoginDTO loginDTo);

​这里的R是一个用于返回给前端的JavaBean类,这个类的几个属性对前端非常重要。

@Data
public class R {
   private long status;

   private String describe ;
   
   private Object message;

    // 成功的数据结构封装 带对象数据
    public static R success(String describe,Object message){
        return new R(200,describe,message);
    }

    // 失败返回的数据 带对象数据
    public static R error(String describe, Object message){
        return new R(500,describe,message);
    }
}

相应地给UserServiceImpl类增加实现方法:

@Override
public R login(LoginDTO loginDTO) {
  String username = loginDTO.getUsername();
  String password = loginDTO.getPassword();
  if(StringUtils.isBlank(username)|| StringUtils.isBlank(password)){
    return R.error("用户名或密码为空");
  }
  LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
  queryWrapper.eq(User::getUsername,username);
  queryWrapper.eq(User::getPassword,password);
  User user = this.getOne(queryWrapper);
  if(user == null){
    return R.error("用户名或密码错误");
  }
  String token = JWTUtils.createToken(user.getId());
  return R.success("登录成功",token);
}

给UserController增加方法:

@PostMapping("/login")
public R login(@RequestBody LoginDTO loginDTO){
  return userService.login(loginDTO);
}
3. 前端收到token后的处理

​构建一个登录表单,注意uni.request()中data参数所传递的数据与LoginDTO类,其回调函数res跟R类的关系。

<template>
	<view>
		<view >
			<input class="border" type="text" placeholder="请输入姓名..." v-model="message.username">
		</view>
		<view>
			<input class="border" type="text" password="true" placeholder="请输入密码..." v-model="message.password">
		</view>
		<view>
			<button type="primary" @click="onSubmit">登录</button>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				message:{
					username:"",
					password:"",
				}
			};
		},
		methods:{
			uni.request({
					url:"http://localhost:8090/user/login",
					method:"POST",
					data:{
						username:this.message.username,
						password:this.message.password
					},
					success:(res)=>{
						console.log(res);
						if(res.data.status!=200){
							uni.showToast({
								title:res.data.describe
							});
						}else{
						    // 后端生产的token,前端收到后保存在本地缓存里
							uni.setStorageSync("token",res.data.message);
						}					
					},
					fail:(err)=>{	
					}
				});
         }
	}
</script>

4. 前端每次发送请求要带上token

uniapp程序​向后端发出请求,需要将收到的token写在请求头的header里面。

getUserInfo(){
  uni.request({
  	url:"http://localhost:8090/user/currentuser",
  	header:{
  		Authorization:uni.getStorageSync("token")
  	},
  	success: (res) => {
 	 	console.log(res);
  	}
 });

给UserController增加方法:

@RequestMapping(value="/currentuser")
public R getUserInfo(@RequestHeader("Authorization") String token){
  User user = userService.getUserByToken(token);
  if(user == null){
    return R.error("token有误");
  }
  return R.success("成功取得用户信息", user);
}

给IUserSevice新增getUserByToken()方法:

User getUserByToken(String token);

​相应的UserServiceImpl的实现方法如下:

@Override
public User getUserByToken(String token) {
    if(StringUtils.isBlank(token)){
      return null;
    }
    Map<String,Object> stringObjectMap = JWTUtils.checkToken(token);
    if(stringObjectMap == null){
      return null;
    }
    // 此处要根据userId查询用户
    int id = (Integer) stringObjectMap.get("userId");
    Long userId = new Long(id);
    User user = this.getById(userId);

    return user;
}

​ 此时后端并未对请求进行拦截处理。

5. 对前端发送来的请求使用拦截器检查token

​ 新建interceptor包,并创建JwtInterceptor.java:

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class JwtInterceptor implements HandlerInterceptor {
   
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        if(StringUtils.isBlank(token)){
           R r = R.error("未登录");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().print(JSON.toJSONString(r));
            return false;
        }
      
        return true;
    }
}

​注册拦截器,在config包下面创建WebMVCConfig.java类:

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
    @Autowired
    private JwtInterceptor jwtInterceptor;
  
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        List<String> pathPatterns=new ArrayList<>();
        pathPatterns.add("/user/currentuser");
      
      	List<String> excludePathPatterns=new ArrayList<>();
        excludePathPatterns.add("/user/login");
      	excludePathPatterns.add("/user/register");
      
        registry.addInterceptor(jwtInterceptor) //添加拦截器
                .addPathPatterns(pathPatterns) //添加要拦截的url
          	    .excludePathPatterns(excludePathPatterns); //添加不需要拦截的url
    }
}

小结:
(1)JWT的重心在后端。在本例中,在很多地方做了简化处理,比如生成的JWT用诸如Redis这样的缓存存储起来,有利于提高效率;对前端传递来的token进行检验,本例仅只是从判空的角度做的。
(2)uniapp前端也有许多改进的地方,比如在前端使用拦截器,对发出的请求统一处理,而不是重复地在uni.request()中写上header。再如对接收到的token可以使用Vuex来处理等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值