整合SpringBoot

搭建SpringBoot+mybatis+jwt环境

1.引入依赖,编写配置

<!-- 引入jwt-->
       <dependency>
           <groupId>com.auth0</groupId>
           <artifactId>java-jwt</artifactId>
           <version>3.4.0</version>
       </dependency>
      <!-- 引入mybatis-->
       <dependency>
           <groupId>org.mybatis.spring.boot</groupId>
           <artifactId>mybatis-spring-boot-starter</artifactId>
           <version>2.1.2</version>
       </dependency>
       <!--引入lombok-->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.16</version>
       </dependency>
      <!-- 引入druid-->
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>druid</artifactId>
           <version>1.1.19</version>
       </dependency>
      <!-- 引入mysql-->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>5.1.38</version>
       </dependency>
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jwt?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456

mybatis.type-aliases-package=com.sovzn.jwttest.entity
mybatis.mapper-locations=classpath:com/sovzn/mapper/*.xml

logging.level.com.sovzn.jwttest.dao

2.开发数据库

CREATE TABLE `jwt`.`user`( 
    `id` INT(11) NOT NULL COMMENT '主键',
    `name` VARCHAR(88) NOT NULL COMMENT '用户名',
    `password` VARCHAR(40) NOT NULL COMMENT '用户密码', 
    PRIMARY KEY (`id`)
) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci; 

INSERT INTO `jwt`.`user` (`id`, `name`, `password`) VALUES ('1', 'sovzn', '123'); 

3.开发entity

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String id;
    private String name;
    private String password;
}

4.开发Dao接口和mapper.xml

@Mapper
public interface UserDao {
    User login(User user);
}
<mapper namespace="com.sovzn.jwttest.dao.UserDao">
    <!--一个简单的查询-->
    <select id="login" parameterType="User" resultType="User">
        select * from user where name=#{name} and password=#{password}
    </select>
</mapper>

5.开发service接口及实现类

public interface UserService {
    User login(User user);
}


@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public User login(User user) {
        //根据用户名和密码查询数据库
        User userDB=userDao.login(user);
        if(userDB!=null){
            return userDB;
        }
        throw new RuntimeException("认证失败");
    }
}

6.开发controller

@RestController
@Slf4j
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/user/login")
    public Map<String,Object> login( User user){
        log.info("用户名:[{}]",user.getName());
        log.info("密码:[{}]",user.getPassword());
        Map<String,Object> map=new HashMap<>();
        try {
            User userDB=userService.login(user);
            // 生成JWT令牌 使用封装的工具类
            Map<String,String> payload =new HashMap<>();
            payload.put("id",userDB.getId());
            payload.put("name",userDB.getName());
           String token= JWTutils.getToken(payload);

            map.put("status",true);
            map.put("msg","认证成功");
            map.put("token",token);
        } catch (Exception e) {
            map.put("status",false);
            map.put("msg",e.getMessage());
        }
        return  map;
    }

    @GetMapping("/user/test")  //测试接口 携带token访问接口
    public Map<String,Object> test(String token){
        Map<String,Object> map=new HashMap<>();
     log.info("当前token:[{}]",token);
        try {
            DecodedJWT verify= JWTutils.getTokenInfo(token); //验证令牌
            map.put("status",true);
            map.put("msg","请求成功");
            /*
            完成要执行的业务逻辑
           */
            return map;
        }
        catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","签名无效");
        }
        catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","令牌过期");
        }
        catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","算法不匹配");
        }
        catch (Exception e){
            e.printStackTrace();
            map.put("msg","token无效");
        }
        map.put("status",false);
        return map;
    }

}

7.在浏览器中模拟登陆

输入URL 
http://localhost:8080/user/login?name=sovzn&password=123

执行结果返回:
{
    "msg": "认证成功",
    "status": true,
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoic292em4iLCJpZCI6IjEiLCJleHAiOjE2MTE0NzgxNDR9.26W7rhPqA5C8wOJ1GH6U40bRJh3l40LMXopth6PsXf4"
}

8.模拟携带token访问接口

输入URL携带登陆后得到的token
http://localhost:8080/user/test?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoic292em4iLCJpZCI6IjEiLCJleHAiOjE2MTE0NzkyMTN9.yoBPr-LZGwdHmdnQ_optlspKSpoxE6dDsCQUUD17MAs

执行结果返回:
{
    "msg": "请求成功",
    "status": true
}

问题?

使用上述方式每次都要传递token数据,每个接口方法都需要验证token,这样显得代码冗余。

优化:

  • 对于web项目可以使用拦截器来优化

  • 对于分布式可以使用网关来优化

1.编写拦截器:

//实现springmvc提供的拦截器接口
public class JWTinterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //官方推荐把jwt 放在请求头里

        //获取请求头里的令牌
        String token = request.getHeader("token");
        Map<String,Object> map=new HashMap<>();
        try {
            JWTutils.getTokenInfo(token); //验证令牌
            return true;//放行
        }
        catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","签名无效");
        }
        catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","令牌过期");
        }
        catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","算法不匹配");
        }
        catch (Exception e){
            e.printStackTrace();
            map.put("msg","token无效");
        }
            map.put("status",false);
        //将map转为json 相应给前台
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().println(json);
        return false;
    }
}

2.配置拦截器

//配置类 实现WebMvcConfigurer
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTinterceptor())//添加拦截器
                .addPathPatterns("/**") //添加拦截的路径
                .excludePathPatterns("/user/login"); //添加放行的路径
    }
}

3.修改测试接口

@PostMapping("/user/test")  //测试接口
  public Map<String,Object> test(HttpServletRequest request){
      Map<String,Object> map=new HashMap<>();
      String token=request.getHeader("token");
      DecodedJWT verify=JWTutils.getTokenInfo(token);
      map.put("用户id",verify.getClaim("id").asString());//然后可以根据得到的用户id进行业务处理
      map.put("status",true);
      map.put("msg","有权访问该接口");
      return map;
  }

测试:

1.浏览器输入URL得到token
http://localhost:8080/user/login?name=sovzn&password=123

结果:
{
    "msg": "认证成功",
    "status": true,
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoic292em4iLCJpZCI6IjEiLCJleHAiOjE2MTE0ODU1Nzh9.NLz6CYM415FrQid7VKCz5y73bDSAtOyrVD8ZW_xDv3c"
}

2.输入URLhttp://localhost:8080/user/test 并将token设置在请求头中

{
    "msg": "有权访问该接口",
    "用户id": "1",
    "status": true
}