简单的Java的token鉴权架构

简单的Java的token鉴权架构

总体时序图

在这里插入图片描述

1、登录

服务端校验密码,成功后存储token到redis(失效时间为1天)并返回token给客户端

2、访问API

通过自定义拦截器,拦截所有请求,并对需要鉴权的API做token鉴权逻辑处理,存在改token则通过请求,不存在则需要做刷新token或生成token处理

鉴权流程

在这里插入图片描述

1、创建token

用户传入appid和appkey来生成token,服务端通过appid和appkey来到mysql中查找该用户的接口权限,如果满足访问该接口权限则生成token并存储到redis,失效时间为1天,并返回给客户端

2、查询token

用户通过appid和appkey来查询token,服务端通过appid和appkey来到mysql中查找该用户的接口权限,如果满足访问该接口权限则根据appid到redis中获取当前的token,并返回给客户端

3、注销token

用户通过appid和appkey来查询token,服务端通过appid和appkey来到mysql中查找该用户的接口权限,如果满足访问该接口权限则到redis中删除对应的token信息

代码参考

自定义用户鉴权注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author gk
 * @note 加上该注解的类http请求时需要进行token鉴权
 * @date 2021/9/16 10:14
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
}

创建、查询、注销接口

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gk
 * @note token的controller
 * @date 2021/6/26 11:14
 */
@RestController
@RequestMapping(value = "/usr/token", produces = "application/json; charset=utf-8")
public class TokenChargingController {

    private final TokenService tokenService;
    public TokenChargingController(TokenService tokenService) {
        this.tokenService = tokenService;
    }

    /**
     * 生成我们自己的token
     * @return
     */
    @RequestMapping(value = "/create_token", method = RequestMethod.GET)
    @ApiOperation(value = "生成token", notes = "生成token值")
    public Object createToken(
            HttpServletRequest request,
            @ApiParam(name = "appid", value = "appid", required = true) @RequestHeader(value = "appid") String appid,
            @ApiParam(name = "appkey", value = "appkey", required = true) @RequestHeader(value = "appkey") String appkey) {
        Map<String, Object> resultMap = new HashMap<>();
        CacheService cacheService = = null;
        try {
            cacheService = new JedisPoolUtils();
            // 初始化:URI、URL、addr
            String uri = request.getRequestURI();
            resultMap.put("AppId", appid);
            resultMap.put("AppKey", appkey);

            // 查询用户权限是否有效:appid、appkey、uri
            ApiUserUriMapping apiUserUriMapping = this.tokenService.getApiUserUriMapping(appid, appkey, uri);
            if (apiUserUriMapping == null) {
                resultMap.put("StatusCode", CommonEnum.SIGNATURE_NOT_MATCH.getResultCode());
                resultMap.put("AccessToken", "");
                resultMap.put("TokenAvailableTime", "");
                resultMap.put("Msg", CommonEnum.SIGNATURE_NOT_MATCH.getResultMsg());
            } else {
                // 创建token
                String token = this.tokenService.createToken(cacheService, appid, appkey);
                resultMap.put("StatusCode", CommonEnum.SUCCESS.getResultCode());
                resultMap.put("AccessToken", token);
                resultMap.put("TokenAvailableTime", cacheService.ttl(token, RedisDbConstant.DB_10) + "");
                resultMap.put("Msg", CommonEnum.SUCCESS.getResultMsg());
            }
        } catch (Exception e) {
            resultMap.put("StatusCode", CommonEnum.INTERNAL_SERVER_ERROR.getResultCode());
            resultMap.put("AccessToken", "");
            resultMap.put("TokenAvailableTime", "");
            resultMap.put("Msg", CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg());
        } finally {
            if (Objects.nonNull(cacheService))
                cacheService.destroy();
        }
        return resultMap;
    }

    /**
     * 查询token
     * @return
     */
    @RequestMapping(value = "/query_token", method = RequestMethod.GET)
    @ApiOperation(value = "获取token", notes = "获取token值")
    public Object queryToken(
            HttpServletRequest request,
            @ApiParam(name = "appid", value = "appid", required = true) @RequestHeader(value = "appid") String appid,
            @ApiParam(name = "appkey", value = "appkey", required = true) @RequestHeader(value = "appkey") String appkey) {
        Map<String, Object> resultMap = new HashMap<>();
        CacheService cacheService = null;
        try {
            cacheService = new JedisPoolUtils();
            // 初始化:URI、URL、addr
            String uri = request.getRequestURI();
            resultMap.put("AppId", appid);
            resultMap.put("AppKey", appkey);

            // 查询用户权限是否有效:appid、appkey、uri
            ApiUserUriMapping apiUserUriMapping = this.tokenService.getApiUserUriMapping(appid, appkey, uri);
            if (apiUserUriMapping == null) {
                resultMap.put("StatusCode", CommonEnum.SIGNATURE_NOT_MATCH.getResultCode());
                resultMap.put("AccessToken", "");
                resultMap.put("TokenAvailableTime", "");
                resultMap.put("Msg", CommonEnum.SIGNATURE_NOT_MATCH.getResultMsg());
            } else {
                // 查询token
                String token = this.tokenService.queryToken(cacheService, appid);
                resultMap.put("StatusCode", CommonEnum.SUCCESS.getResultCode());
                resultMap.put("AccessToken", token);
                resultMap.put("TokenAvailableTime", cacheService.ttl(token, RedisDbConstant.DB_10) + "");
                resultMap.put("Msg", CommonEnum.SUCCESS.getResultMsg());
            }
        } catch (Exception e) {
            resultMap.put("StatusCode", CommonEnum.INTERNAL_SERVER_ERROR.getResultCode());
            resultMap.put("AccessToken", "");
            resultMap.put("TokenAvailableTime", "");
            resultMap.put("Msg", CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg());
        } finally {
            if (Objects.nonNull(cacheService))
                cacheService.destroy();
        }
        return resultMap;
    }
    
    @RequestMapping(value = "/logout_user", method = RequestMethod.GET)
    @ApiOperation(value = "用户注销", notes = "用户注销")
    public Object logoutUser(
            HttpServletRequest request,
            @ApiParam(name = "appid", value = "appid", required = true) @RequestHeader(value = "appid") String appid,
            @ApiParam(name = "appkey", value = "appkey", required = true) @RequestHeader(value = "appkey") String appkey) {
        Map<String, Object> resultMap = new HashMap<>();
        CacheService cacheService = null;
        try {
            cacheService = new JedisPoolUtils();
            // 初始化:URI、URL、addr
            String uri = request.getRequestURI();
            resultMap.put("AppId", appid);
            resultMap.put("AppKey", appkey);

            // 查询用户权限是否有效:appid、appkey、uri
            ApiUserUriMapping apiUserUriMapping = this.tokenService.getApiUserUriMapping(appid, appkey, uri);
            if (apiUserUriMapping == null) {
                resultMap.put("StatusCode", CommonEnum.SIGNATURE_NOT_MATCH.getResultCode());
                resultMap.put("AccessToken", "");
                resultMap.put("TokenAvailableTime", "");
                resultMap.put("Msg", CommonEnum.SIGNATURE_NOT_MATCH.getResultMsg());
            } else {
                // 创建token
                boolean isSuccess = this.tokenService.logoutUser(cacheService, appid);
                resultMap.put("StatusCode", isSuccess ? CommonEnum.SUCCESS.getResultCode() : CommonEnum.INTERNAL_SERVER_ERROR.getResultCode());
                resultMap.put("AccessToken", "");
                resultMap.put("TokenAvailableTime", "");
                resultMap.put("Msg", isSuccess ? CommonEnum.SUCCESS.getResultMsg() : CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg());
            }
        } catch (Exception e) {
            resultMap.put("StatusCode", CommonEnum.INTERNAL_SERVER_ERROR.getResultCode());
            resultMap.put("AccessToken", "");
            resultMap.put("TokenAvailableTime", "");
            resultMap.put("Msg", CommonEnum.INTERNAL_SERVER_ERROR.getResultMsg());
        } finally {
            if (Objects.nonNull(cacheService))
                cacheService.destroy();
        }
        return resultMap;
    }
}

接口服务及实现类

public interface TokenService {

    /**
     * 权限验证:查询用户的接口访问权限信息
     *
     * @param appid  用户ID
     * @param appkey 用户KEY
     * @param uri    请求路径URI
     * @return ApiUserUriMapping
     */
    ApiUserUriMapping getApiUserUriMapping(
            String appid,
            String appkey,
            String uri);

    /**
     * 创建token
     *
     * @param appid
     * @param appkey
     * @return token
     */
    String createToken(CacheService cacheService, String appid, String appkey) throws Exception;

    /**
     * 查询token
     *
     * @param appid
     * @return token
     */
    String queryToken(CacheService cacheService, String appid) throws Exception;


    /**
     * 查询token
     *
     * @param appid
     * @return token
     */
    boolean logoutUser(CacheService cacheService, String appid) throws Exception;
}
@Service
public class TokenServiceImpl implements TokenService {

    private final VBApiUserUriMappingMapper vbApiUserUriMappingMapper;
    @Autowired
    public ApiUserUriMappingServiceImpl(
            VBApiUserUriMappingMapper vbApiUserUriMappingMapper) {
        this.vbApiUserUriMappingMapper = vbApiUserUriMappingMapper;
    }

    @Override
    public ApiUserUriMapping getApiUserUriMapping(String appid, String appkey, String uri) {
        List<Map> lists = this.getVBApiUserUriMappingByMap(
                new HashMap<String, String>() {
                    {
                        put("appid", appid);
                        put("appkey", appkey);
                        put("uri", uri);
                    }
                }
        );
        return CollectionUtils.isEmpty(lists) ?
                null : JSONObject.parseObject(JSONObject.toJSONString(lists.get(0)), ApiUserUriMapping.class);
    }

    @Override
    public String createToken(CacheService cacheService, String user, String key) throws Exception {
        String token = "";
        Random random = new Random();
        double v = random.nextDouble();
        //获取当前时间时间
        String currentDate = DateUtils.getCurrentDate("yyyy-MM-dd HH:mm:ss");
        String md = user + "+" + key + "#" + currentDate + "%" + v + "@";
        //md5加密
        String mdToken = Md5Util.MD5(md);
        //MD5二次加密
        token = Md5Util.MD5(mdToken);

        //删除老的token
        String oldToken = cacheService.get(TokenConstant.CURRENT_TOKEN_PREFIX + user, RedisDbConstant.DB_10);
        if (StringUtils.isNotBlank(oldToken))
            cacheService.del(oldToken, RedisDbConstant.DB_10);

        //更新redis
        cacheService.hset(token, UserFieldConstant.APP_ID, user, RedisDbConstant.DB_10);
        cacheService.hset(token, UserFieldConstant.APP_KEY, key, RedisDbConstant.DB_10);
        cacheService.hset(token, UserFieldConstant.TOKEN, token, RedisDbConstant.DB_10);
        cacheService.hset(token, UserFieldConstant.UPDATE_TIME, currentDate, RedisDbConstant.DB_10);
        //设置失效时间
        cacheService.expire(token, TokenConstant.ONE_DAY, RedisDbConstant.DB_10);
        //设置当前用户使用的token
        cacheService.set(TokenConstant.ONE_DAY, TokenConstant.CURRENT_TOKEN_PREFIX + user, token, RedisDbConstant.DB_10);
        return token;
    }

    @Override
    public String queryToken(CacheService cacheService, String appid) throws Exception {
        String token = "";
        String redisKey = TokenConstant.CURRENT_TOKEN_PREFIX + appid;
        if (cacheService.exists(redisKey, RedisDbConstant.DB_10))
            token = cacheService.get(redisKey, RedisDbConstant.DB_10);
        return token;
    }

    @Override
    public boolean logoutUser(CacheService cacheService, String appid) throws Exception {
        boolean isLogout = false;
        String redisKey = TokenConstant.CURRENT_TOKEN_PREFIX + appid;
        if (cacheService.exists(redisKey, RedisDbConstant.DB_10))
            cacheService.del(redisKey, RedisDbConstant.DB_10);
        isLogout = true;
        return isLogout ;
    }

    private List<Map> getVBApiUserUriMappingByMap(Map map) {
        return this.vbApiUserUriMappingMapper.getAllByParam(
                ObjectUtils.isEmpty(map.get("id")) ? null : map.get("id").toString(),
                ObjectUtils.isEmpty(map.get("appid")) ? null : map.get("appid").toString(),
                ObjectUtils.isEmpty(map.get("appkey")) ? null : map.get("appkey").toString(),
                ObjectUtils.isEmpty(map.get("userType")) ? null : map.get("userType").toString(),
                ObjectUtils.isEmpty(map.get("userName")) ? null : map.get("userName").toString(),
                ObjectUtils.isEmpty(map.get("userUnit")) ? null : map.get("userUnit").toString(),
                ObjectUtils.isEmpty(map.get("phone")) ? null : map.get("phone").toString(),
                ObjectUtils.isEmpty(map.get("createTime")) ? null : map.get("createTime").toString(),
                ObjectUtils.isEmpty(map.get("updTime")) ? null : map.get("updTime").toString(),
                ObjectUtils.isEmpty(map.get("beginTime")) ? null : map.get("beginTime").toString(),
                ObjectUtils.isEmpty(map.get("endTime")) ? null : map.get("endTime").toString(),
                ObjectUtils.isEmpty(map.get("accessIpList")) ? null : map.get("accessIpList").toString(),
                ObjectUtils.isEmpty(map.get("uri")) ? null : map.get("uri").toString(),
                ObjectUtils.isEmpty(map.get("uriUserConfig")) ? null : map.get("uriUserConfig").toString(),
                ObjectUtils.isEmpty(map.get("formatCls")) ? null : map.get("formatCls").toString(),
                ObjectUtils.isEmpty(map.get("opType")) ? null : map.get("opType").toString());
    }
}

自定义登录拦截器

/**
 * @author gk
 * @note 登陆过滤器
 * @date 2021/09/16 11:14
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private ApiUserUriService apiUserUriService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        // 如果打上了AuthToken注解则需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {

            CacheService cacheService = new JedisPoolUtils();
            try {
                //从header中拿token,并进行解析
                String token = request.getHeader(TokenConstant.TOKEN);

                if (StringUtils.isBlank(token)) {
                    reSetResponse(response, CommonEnum.SIGNATURE_NOT_MATCH);
                    return false;
                }
                if (!cacheService.exists(token, RedisDbConstant.DB_10)) {
                    reSetResponse(response, CommonEnum.NO_USER);
                    return false;
                }
                String uri = request.getRequestURI();

                String appid = cacheService.hget(token, UserFieldConstant.APP_ID, RedisDbConstant.DB_10);
                String appkey = cacheService.hget(token, UserFieldConstant.APP_KEY, RedisDbConstant.DB_10);

                if (StringUtils.isBlank(appid) || StringUtils.isBlank(appkey)) {
                    reSetResponse(response, CommonEnum.NOT_PERMISSION);
                    return false;
                }

                // 查询用户权限是否有效:appid、appkey、uri
                ApiUserUriMapping apiUserUriMapping = this.apiUserUriService.getApiUserUriMapping(appid, appkey, uri);
                if (Objects.isNull(apiUserUriMapping)) {
                    reSetResponse(response, CommonEnum.NOT_PERMISSION);
                    return false;
                }

                //dataRange|appid|appkey
                String dataRange = apiUserUriMapping.getUriUserConfig();
                Map<String, String> key = new HashMap<>();
                key.put("dataRange", dataRange);
                key.put("appid", appid);
                key.put("appkey", appkey);
                key.put("token", token);
                key.put("className", apiUserUriMapping.getFormatCls());
                request.setAttribute(TokenConstant.REQUEST_CURRENT_KEY, key);
            } catch (Exception e) {
                e.printStackTrace();
                reSetResponse(response, CommonEnum.INTERNAL_SERVER_ERROR);
                return false;
            } finally {
                cacheService.destroy();
            }

            return true;
        }

        if (StringUtils.isNotBlank(request.getRequestURI()) && request.getRequestURI().endsWith("/v2/api-docs")) {
            //swagger自带的api-docs
            // 查询用户权限是否有效:appid、appkey、uri
            String appid = request.getHeader("appid");
            String appkey = request.getHeader("appkey");
            if (StringUtils.isBlank(appid) || StringUtils.isBlank(appkey)) {
                reSetResponse(response, CommonEnum.SIGNATURE_NOT_MATCH);
                return false;
            }

            // 查询用户权限是否有效:appid、appkey、uri
            ApiUserUriMapping mapping = this.apiUserUriService.getApiUserUriMapping(appid, appkey, request.getRequestURI());
            if (Objects.isNull(mapping)) {
                reSetResponse(response, CommonEnum.NOT_PERMISSION);
                return false;
            }

        }
        request.setAttribute(TokenConstant.REQUEST_CURRENT_KEY, null);

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    }

    /**
     * 登录拦截 重置响应数据
     *
     * @param response
     */
    private void reSetResponse(HttpServletResponse response, CommonEnum commonEnum) {
        PrintWriter pw = null;
        try {
            response.reset();
            response.setContentType("application/json;charset=UTF-8");
            ServiceResult serviceResult = new ServiceResult(commonEnum.getResultCode(), commonEnum.getResultMsg());
            serviceResult.insertResult(null);
            pw = response.getWriter();
            pw.write(JSONObject.toJSONString(serviceResult));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (pw != null) {
                pw.flush();
                pw.close();
            }
        }
    }
}

配置类配置

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @author gk
 * @note 配置类
 * @date 2021/7/20 11:14
 */
@Configuration
public class WebConfigurer implements WebMvcConfigurer {

    @Resource
    private LoginInterceptor loginInterceptor;

    //注册登录拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**");//添加所需要拦截的接口,这里我们拦截所有,但是
//                .excludePathPatterns("/**/evcs/v1/query_token/**"); //不拦截登陆和退出请求
    }

    //页面跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
    }

    //配置静态资源
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    }
}

调用方法

在接口方法上使用@AuthToken该注解

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值