java脑洞 springboot 轻量级JWT安全框架

脑洞的由来

场景一:项目转用JWT做权限认证,刚开始选用shiro+jwt,但是发现对于一个无状态认证来说,shiro太重了,原本便捷的功能反而显得很多余

功能需求

  1. 从请求中获得token
  2. 校验token合法性和时效性
  3. 拦截请求校验权限

Git地址

https://gitee.com/wqrzsy/lp-demo/tree/master/lp-springboot-jwt

更多demo请关注

springboot demo实战项目
java 脑洞

功能实现

整体项目结构


17317532-172b2ff2fa0825c1.png
image.png

可以看到用到的类很少,下面列举下关键类

权限校验注解

/**
 * 此注解用于Controller接口方法上,标记为需要登录
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface NeedLogin {

    /**
     * 需要的权限
     */
    int[] value() default {};

}

web拦截器

/**
 * web拦截器
 */
public abstract class WebAuthInterceptor implements HandlerInterceptor {

    /**
     * 此处省略
     */

}

账户抽象接口


17317532-88493f81fff9a2b4.png
image.png

实际运用

接下来我们实际运用时还需要哪些东西

  1. 首先是拦截器
/**
 * web拦截器的基础实现
 * Created by qirong on 2019/5/31.
 */
@Component
public class CommonWebAuthInterceptor extends WebAuthInterceptor {

    @Autowired
    private AccountManager accountManager;

    /**
     * 校验token并获取用户信息
     * @param token
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    protected Optional<AccountInfo> verifyAndGetUser(String token, HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        Long uid = JwtUtils.getUid(token);
        if (uid == null) {
            return Optional.empty();
        }
        // 根据id获取账户
        Account account = accountManager.getAccount(uid);
        // 校验Token
        if (JwtUtils.verifyToken(token, account.getCredentialsSalt())) {
            return Optional.of(account);
        } else {
            return Optional.empty();
        }
    }

    /**
     * 未登录流程
     *
     * @param token
     * @param request
     * @param response
     * @param handler
     */
    @Override
    protected void unAuthorizedProcess(String token, HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        throw new AuthException("未登录");
    }

    /**
     * 获取所需权限
     *
     * @param handler
     * @return
     */
    @Override
    protected List<Permission> getNeedPermissions(AccountInfo accountInfo, HandlerMethod handler) {
        NeedLogin needLogin = handler.getMethodAnnotation(NeedLogin.class);
        Assert.notNull(needLogin, () -> "");
        List<Permission> permissionList = new ArrayList<>();
        // 获取注解上所有权限
        for (int id : needLogin.value()) {
            // 将权限转换为枚举
            AccountPermission permission = AccountPermission.getByID(id);
            Assert.notNull(permission, "权限转换错误, 权限ID :" + id + ",方法 : " + handler.getMethod().getName());
            permissionList.add(permission);
        }
        return permissionList;
    }

    /**
     * 校验权限
     *
     * @param accountInfo
     * @param needPermissions
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean verifyPermissions(AccountInfo accountInfo, List<Permission> needPermissions, HttpServletRequest request, HttpServletResponse response) {
        // 从账户对象上获取权限列表
        List<Permission> hadPermissionList = accountInfo.getPermissions();
        for (Permission permission : needPermissions) {
            // 判断权限列表是否包含所需权限
            if (!hadPermissionList.contains(permission)) {
                return false;
            }
        }
        // 成功
        return true;
    }

    /**
     * 拒绝流程
     *
     * @param token
     * @param request
     * @param response
     * @param handler
     */
    @Override
    protected void forbiddenProcess(String token, HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        throw new AuthException("没有权限,拒绝登录");
    }

}

  1. 拦截器中用到的自定义异常和全局异常处理
/**
 * 自定义权限异常
 * Created by qirong on 2019/6/3.
 */
public class AuthException extends RuntimeException {

    public AuthException() {
        super();
    }

    public AuthException(String msg) {
        super(msg);
    }

    public AuthException(Throwable throwable) {
        super(throwable);
    }

}
/**
 * 全局异常拦截
 */
@ControllerAdvice
public class WebExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(WebExceptionHandler.class);

    /**
     * 权限异常
     */
    @ExceptionHandler(value = AuthException.class)
    @ResponseBody
    public String onException(HttpServletRequest req, Exception e) {
        logException(req, e);
        return e.getMessage();
    }

    private void logException(HttpServletRequest req, Exception e) {
        if (LOG.isErrorEnabled()) {
            LOG.error("请求异常:[uri={},method={},e={}]", req.getRequestURI(), req.getMethod(), e.getClass());
            LOG.error(e.getMessage());
            LOG.error("StackTrace: {}", e);
        }
    }

}

然后我们来看下测试用的Controller

@RestController
@ApiOperation(value = "测试接口")
public class TestController {

    @Autowired
    private AccountManager accountManager;

    @GetMapping("testAuth")
    @ApiOperation(value = "测试登录服务", notes = "测试登录服务")
    public String testAuth() {
        // 生成Token
        Account account = accountManager.createAccount();
        return JwtUtils.sign(account.getUid(), account.getCredentialsSalt(), 30000);
    }

    @NeedLogin(SysPermission.TEST_PERMISSION_1)
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "lp_token", value = "令牌", required = true)
    })
    @GetMapping("testPermission")
    @ApiOperation(value = "测试权限服务", notes = "测试权限服务")
    public String testPermission() {
        return "success";
    }

    @NeedLogin({SysPermission.TEST_PERMISSION_1, SysPermission.TEST_PERMISSION_2})
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "lp_token", value = "令牌", required = true)
    })
    @GetMapping("testNoPermission")
    @ApiOperation(value = "测试无权限服务", notes = "测试无权限服务")
    public String testNoPermission() {
        return "success";
    }

}

测试

http://localhost:8080/swagger-ui.html

demo项目导入

参考: https://www.jianshu.com/p/cd0275a2f5fb

如果这篇文章对你有帮助请给个star


17317532-71647f21790a7e70.png
image.png

转载于:https://www.jianshu.com/p/c454b7709410

猜你喜欢

转载自blog.csdn.net/weixin_33877885/article/details/91287975
今日推荐