ssm中使用拦截器跟自定义注解实现前端token校验

最近做的一个项目用的是前后端分离的开发模式,系统是要登录后才能进行操作的,所以需要进行身份token校验,校验通过后才能得到所请求的资源。我一开始想到的是使用过滤器实现,但系统里的有些controller是不用过滤的,比如一个新增用户的页面,有很多个下拉框,那请求下拉框数据的时候,就不用每个controller都进行校验了。

于是就用了拦截器+自定义注解来实现,思路如下:用户登录成功后生成一个jwt,也就是token,然后存到Redis里去;每次发起请求时首先判断该controller上有没有自定义的注解,如果有则跳过token验证,如果没有则不跳过,然后去Redis里取token,取到了则验证成功,取不到则验证失败

自定义注解代码如下(ExcludeInterceptor.java):

package com.ue.core.web.interceptor;

import java.lang.annotation.*;

/**
 * 用于排除不用进行jwt验证的自定义注解
 * @author LiJun
 * @Date 2019年09月28日
 * @Time 11:08
 */
@Target(ElementType.METHOD)//指定被修饰的Annotation可以放置的位置:方法上面
@Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Inherited//指定被修饰的Annotation将具有继承性
public @interface ExcludeInterceptor {
}

拦截器相关逻辑如下(JwtInterceptor.java):

package com.ue.core.web.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ue.core.dao.UserDao;
import com.ue.core.service.JwtService;
import com.ue.core.util.BeanUtil;
import com.ue.core.util.JedisUtil;
import com.ue.core.util.JsonRespData;
import com.ue.core.util.SpringContextHolder;
import com.ue.core.web.interceptor.ExcludeInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;

import static com.ue.core.entity.CommonEntity.*;

/**
 * 用于处理jwt验证的拦截器
 * @author LiJun
 * @Date 2019年09月27日
 * @Time 18:06
 */
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {
    private UserDao userDao;//定义用户的dao层
    private JwtService jwtService;
    private boolean OFF = false;//true为关闭jwt令牌验证功能

    /**
     * 返回 false:请求被拦截,返回
     * 返回 true :请求OK,可以继续执行,放行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        Jedis jedis = null;
        try {
            //获取到目标方法对象
            HandlerMethod method = (HandlerMethod) o;
            //拿到方法上的注解
            ExcludeInterceptor methodAnnotation = method.getMethodAnnotation(ExcludeInterceptor.class);
            if (OFF || BeanUtil.isNotBlank(methodAnnotation)) {//如果该方法上有自定义的注解,则直接跳过这个拦截器
                return true;
            }

            jedis = JedisUtil.getJedisConn();
            String jwt = request.getHeader("jwt");//请求头里的jwt令牌
            String userId = request.getParameter("tokenId");//登录者的ID

            jwtService = SpringContextHolder.getBean(JwtService.class);//从静态变量applicationContext中取到JwtService
            userDao = SpringContextHolder.getBean(UserDao.class);//从静态变量applicationContext中取到UserDao

            if (StringUtils.isNotBlank(jwt)){//jwt不为空
                if (StringUtils.isBlank(jedis.get(jwt))){//如果Redis里没有这个jwt,说明jwt已经失效
                    returnErrorResponse(response,JsonRespData.success(REQUEST_204, "token已过期"));
                    return false;
                }
                else {//jwt没有失效
                    Map user = userDao.getUserById(userId);
                    String isDelete = String.valueOf(user.get("is_delete"));
                    String state = String.valueOf(user.get("state"));

                    if ("N".equals(isDelete) && "2".equals(state)) {//该用户没有被逻辑删除且审核已通过
                        DecodedJWT decodedJWT = jwtService.getDecryptString(jwt);//token解密id
                        String id = decodedJWT.getClaim("id").asString();
                        if (StringUtils.isNotBlank(userId) && userId.equals(id)) {
                            jedis.expire(jwt, 1800);//验证用户的登录状态成功,token有效期重置为30分钟
                            return true;
                        } else {
                            returnErrorResponse(response,JsonRespData.success(REQUEST_203, "用户登录异常,用户id前后不一致"));
                            return false;
                        }
                    } else {
                        log.info("帐号已被管理员禁用,token有效期不重置");
                        returnErrorResponse(response,JsonRespData.success("202", "该帐号暂时不能进行登录"));
                        return false;
                    }
                }
            }
            else {
                log.info("入参jwt为空");
                returnErrorResponse(response,JsonRespData.success(REQUEST_205, "token不允许为空"));
                return false;
            }
        }catch (Exception e){
            log.info("验证jwt出现异常:" + e);
            returnErrorResponse(response,JsonRespData.success(INTERNAL_SERVER_ERROR, "验证jwt出现异常"));
            return false;
        }finally {
            if (jedis != null)
                jedis.close();
        }
    }

    /**
     * 发送jwt的验证结果到客户端
     * @author LiJun
     * @Date 2019/9/28
     * @Time 15:13
     * @param response
     * @param result
     * @return void
     */
    private void returnErrorResponse(HttpServletResponse response, JsonRespData result) throws IOException {
        OutputStream out = null;
        try{
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/json");
            out = response.getOutputStream();
            out.write(JSONObject.toJSONString(result).getBytes("utf-8"));
            out.flush();
        } finally{
            if(out != null){
                out.close();
            }
        }
    }

    /**
     * 请求controller之后,渲染视图之前
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 请求controller之后,视图渲染之后
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

在springmvc中配置拦截器(spring-web.xml):

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.ue.core.web.interceptor.JwtInterceptor"></bean>
        </mvc:interceptor>
</mvc:interceptors>

登录相关代码如下:

UserController.java:

    /**
     * APP端登录验证
     * @author LiJun
     * @Date 2019/9/25
     * @Time 9:35
     * @param user
     * @return com.ue.core.util.JsonRespData
     */
    @RequestMapping(value = "login",method = RequestMethod.POST)
    @ResponseBody
    @ExcludeInterceptor
    @ApiOperation(value = "APP端登录验证", notes = "登录", response = JsonRespData.class)
    public JsonRespData login(User user){
        return userService.login(user);
    }

UserService.java:

    /**
     * APP端登录验证
     * @author LiJun
     * @Date 2019/9/25
     * @Time 9:35
     * @param user
     * @return com.ue.core.util.JsonRespData
     */
    public JsonRespData login(User user){
        String name = user.getName();//用户输入的用户名
        String password = user.getPassword();//用户输入的密码
        User userInfo = userDao.getPwdByName(name);
        if (BeanUtil.isNotBlank(userInfo)) {
            Integer state = userInfo.getState();
            String isDelete = userInfo.getIsDelete();
            if (state != 2) {//如果用户状态不是“已通过”
                //0:禁用、1:待审核、2:审核已通过、3:审核不通过
                return JsonRespData.success(REQUEST_208, "该帐号暂时不能进行登录");
            }
            else if ("Y".equals(isDelete)){//如果用户已被删除
                //Y:未删除、N:已删除
                return JsonRespData.success(REQUEST_209, "账号已删除");
            }
            else {
                Jedis jedis = null;
                try {
                    String pwd = userInfo.getPassword();
                    if (pwd.equals(password)) {//登陆成功,添加token到redis管理
                        jedis = JedisUtil.getJedisConn();
                        String id = userInfo.getId().toString();
                        String accessUrl = "暂未指定";
                        String token = jwtService.getEncryptString(id, accessUrl);//生成token
                        jedis.set(token, token);//将token保存到Redis里
                        jedis.expire(token, 3600);
                        userInfo.setToken(token);
                        return JsonRespData.success(REQUEST_SUCCESS, "登录成功", userInfo);
                    } else {//登录失败
                        return JsonRespData.success(REJECT_REQUEST, "帐号或密码错误");
                    }
                } catch (Exception e) {
                    log.error("登录验证发生异常:" + e);
                    return JsonRespData.success(INTERNAL_SERVER_ERROR, "服务器发生错误");
                }finally {
                    if (jedis != null)
                        jedis.close();
                }
            }
        } else {
            return JsonRespData.success(REJECT_REQUEST, "帐号未注册");
        }
    }

自定义注解的使用:

使用自定义注解期间遇到的类型转换异常:

java.lang.ClassCastException: org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler cannot be cast to org.springframework.web.method.HandlerMethod

这是JwtInterceptor.java中HandlerMethod method = (HandlerMethod) o强制转换时的异常,出现原因是:所请求的资源不存在,也就是说系统中没有所请求的controller

解决方案:在强转前加上if (o instanceof HandlerMethod)判断一下,如果请求的资源不存在则不进行处理。

发布了102 篇原创文章 · 获赞 64 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_42687829/article/details/102717073