脑洞的由来
场景一:项目转用JWT做权限认证,刚开始选用shiro+jwt,但是发现对于一个无状态认证来说,shiro太重了,原本便捷的功能反而显得很多余
功能需求
- 从请求中获得token
- 校验token合法性和时效性
- 拦截请求校验权限
Git地址
https://gitee.com/wqrzsy/lp-demo/tree/master/lp-springboot-jwt
更多demo请关注
功能实现
整体项目结构
可以看到用到的类很少,下面列举下关键类
权限校验注解
/**
* 此注解用于Controller接口方法上,标记为需要登录
*/
@Retention(RUNTIME)
@Target(METHOD)
public @interface NeedLogin {
/**
* 需要的权限
*/
int[] value() default {};
}
web拦截器
/**
* web拦截器
*/
public abstract class WebAuthInterceptor implements HandlerInterceptor {
/**
* 此处省略
*/
}
账户抽象接口
实际运用
接下来我们实际运用时还需要哪些东西
- 首先是拦截器
/**
* 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("没有权限,拒绝登录");
}
}
- 拦截器中用到的自定义异常和全局异常处理
/**
* 自定义权限异常
* 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
转载于:https://www.jianshu.com/p/c454b7709410