从零开始的SpringBoot前后端分离入门级项目(三)

懵懂的女人 提交于 2020-10-27 05:25:20

实体类编写

在前面的文章中我们已经完成了项目目录和基本框架的搭建,现在我们开始编写实体类,首先我们在model包下创建一个pojo子包。
注意:关于PO、VO、POJO、DTO等概念网络上已经有很多资料了,在本项目中不再阐述其的详细含义,且为了方便起见只划分POJO与DTO,为此产生的一些有歧义或错误的划分方法或用法请读者们见谅。
创建好子包之后我们创建相应的pojo,一张表对应一个类,如果编写过Web项目的同学对这步应该是比较熟悉的,创建相应的类的过程笔者不一一讲解,具体可以参见笔者的GitHub主页,后续将会上传完整的直接导入的工程代码,创建完毕后请务必将@Data、@AllArgsConstructor、@NoArgsConstructor三个注解加上。
pojo创建完毕


利用Token进行用户鉴权

实体类编写完之后我们开始进行用户权限认证功能的编写,在本项目中利用了jwt和拦截器(interceptor)进行实现。
首先在本项目仅分为三种角色:

  1. 实验室管理员(ADMIN)
  2. 实验室成员(VIP)
  3. 普通用户(MEMBER)

这种划分方式是不够严谨和安全的,目前的权限划分一般是分为用户角色和用户权限两个部分,每个用户对应某个角色,某个角色拥有某些权限,欢迎读者自行思考更完善的权限划分方案。

创建Jwt工具类

首先我们先前往util包中创建一个名为JwtUtil的类,类的内容如下:
JwtUtil

/**
 * @Author Alfalfa99
 * @Date 2020/9/13 15:54
 * @Version 1.0
 * JWT生成以及校验工具类
 */

@ConfigurationProperties("jwt.config")
@Component
public class JwtUtil {
   
   
    private String key;
    private long ttl;

    public String getKey() {
   
   
        return key;
    }

    public void setKey(String key) {
   
   
        this.key = key;
    }

    public long getTtl() {
   
   
        return ttl;
    }

    public void setTtl(long ttl) {
   
   
        this.ttl = ttl;
    }

    /**
     * 生成JWT
     *
     * @param id
     * @return
     */
    public String createJWT(String id, String roles) {
   
   
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        JwtBuilder builder = Jwts.builder().setId(id)	//在这里我们将用户的id存入Jwt中,方便后续使用
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256, key).claim("roles", roles); //在这里我们将用户的角色存入Jwt中,方便后续鉴权,如果想存别的内容也可以往里写
        if (ttl > 0) {
   
   
            builder.setExpiration(new Date(nowMillis + ttl));
        }
        return builder.compact();
    }

    /**
     * 解析JWT
     *
     * @param jwtStr
     * @return
     */
    public Claims parseJWT(String jwtStr) {
   
   
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwtStr)
                .getBody();
    }
}

key是服务端用于加密的一个密文,通常随机生成,ttl是token的过期时间,token的详细原理请自行查阅学习,篇幅有限不再赘述。在这里我们利用了@ConfigurationProperties("jwt.config")注解从application.yml配置文件中读取了如下配置并自动注入字段

jwt:
  config:
    key: SecretKey #服务端加密所使用的密文(自拟)
    ttl: 21600000 #毫秒
#请将该段复制至application.yml以便于能够启动项目

回到token,token是用户在成功登录后读取用户的角色和id生成得来的,生成的token应该在登陆成功后返回给前端,前端之后每次访问接口都应该带上这个token。
注意:千万不要使用token存储用户的敏感信息,token只能用于避免被篡改,而不用于加密!token本身携带的内容是可以在没有服务端秘钥的情况下直接解析出来的!!!
这时候我们就可以实例化JwtUtil并使用createJWT生成token或者使用parseJWT解析token了

利用拦截器(interceptor)实现用户访问的拦截与鉴权

在上文中我们已经能够解析与生成一个token了,但是如果我们在每一个接口都重新解析token进行权限认证将过于繁琐且损耗服务器性能,如果之前有过JavaEE项目编写经验的读者应该学习过过滤器(Filter),拦截器是Spring框架为我们提供的一种类似于Filter的组件但拦截器的功能更多更强劲,话不多说直接怼代码,首先我们在主启动类的同级目录创建一个interceptor包用于存放我们的拦截器,并在这个包下创建一个名为Tokeninterceptor的类,类的内容:

/**
 * @Author Alfalfa99
 * @Date 2020/9/13 18:18
 * @Version 1.0
 * 全局校验Token
 */
@Component
public class TokenInterceptor implements HandlerInterceptor {
   
   

    private final JwtUtil jwtUtil;

    public TokenInterceptor(JwtUtil jwtUtil) {
   
   
        this.jwtUtil = jwtUtil;
    }

    /**
     * 通过拦截器对请求头进行校验
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
   
   
        String header = request.getHeader("Authorization");
        if (header != null && !"".equals(header)) {
   
   
            if (header.startsWith("Bearer ")) {
   
   
                //获得token
                String token = header.substring(7);
                //验证token
                try {
   
   
                    Claims claims = jwtUtil.parseJWT(token);
                    String roles = (String) claims.get("roles");
                    if (roles != null) {
   
   
                        request.setAttribute("uid",claims.getId());
                        request.setAttribute("roles",roles);
                        return true;
                    } else {
   
   
                        throw new BadCredentialsException("令牌已失效");
                    }
                } catch (Exception e) {
   
   
                    throw new BadCredentialsException("令牌已失效");
                }
            }
        }
        throw new AuthenticationCredentialsNotFoundException("请先登录");
    }
}

前端每次请求时token应该是放置于请求头中键名为Authorization, 值为Bearer Token(Token为服务端生成的token)。

首先我们使用@Component注解将该类注册为一个Spring容器,然后使用该类实现 HandlerInterceptor接口,preHandle说明我们是在目标方法执行前进行处理。我们先通过构造器的方式将JwtUtil注入便于后续使用,然后从请求头中读取token,如果token为空则说明用户没有登陆,直接抛出异常即可,如果在解析token出现错误直接抛出错误即可。这一个部分的主要思路就是如果不能正确的解析出token的内容则返回给前端错误信息,让前端跳转到登录页面进行重新登录,如果能够正确解析出我们存储的用户id和用户角色这两个内容则添加到请求request域里面去,后续我们在接口处的直接可以从request中读到用户iduid和用户角色roles
这样我们就可以判断每次访问时用户是否已经登陆了,并且我们将用户id以及用户角色都存于request中便于我们之后controller的开发。

编写InterceptorConfig

在完成TokenInterceptor类的编写之后我们关于Token鉴权的内容就基本结束了,当然我们还需要编写一个InterceptorConfig类去配置我们的拦截器,那么我们在config包中创建一个名为InterceptorConfig的类,类的内容如下:

/**
 * @author 苜蓿
 * @date 2020/9/13
 * 拦截器配置类
 */
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
   
   

    private final TokenInterceptor tokenInterceptor;

    public InterceptorConfig(TokenInterceptor tokenInterceptor) {
   
   
        this.tokenInterceptor = tokenInterceptor;
    }


    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
   
   
        //拦截所有目录,除了通向login和register的接口
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/**/login/**", "/**/register/**")
                .excludePathPatterns("/**/*.html", "/**/*.js", "/**/*.css");
}

那么我们的TokenInterceptor就已经配置完成了,拦截除了html文件、css文件、js文件以及登录注册接口之外的所有访问请求并进行鉴权。

那么本篇博客的内容也已经完成了,在下一篇博客我们就开始进入controller的编写。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!