fpassport-lib是根据token机制实现的WEB登录权限校验模块。
Passport for Java WEB project via userId and token
为什么需要token机制来进行权限校验呢?
在分布式环境中,因为站点是集群的,不可能使用tomcat的session来保存用户的登录状态。微服务架构也需要
做成无状态的服务,这就需要token机制来做用户权限校验了。
这里,基于Spring的Interceptor来拦截指定的需要权限验证的
Controller里的method,去校验userId + token。就可以实现它。
1.Spring + Spring MVC + myBatis. 可用于SpringMVC或者SpringBoot项目
2.maven3.0
3.Jdk1.8+
4.mysql或者其他的数据库
5.Redis
如何使用fpassport-lib库,参考项目fpassport-site
Usage:
Step1:
git clone https://github.com/flylib/fpassport.git
Step2: 安装fpassport-lib.jar到本地的maven仓库
进入fpassport-lib目录
cd fpassport-lib
mvn install
Step3: 在你自己的SpringMVC或者SpringBoot项目中引用fpassport-lib.jar
在自己的项目的pom.xml中增加下面的依赖
<dependency>
<groupId>com.appjishu</groupId>
<artifactId>fpassport-lib</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
Step4. 数据库建表SQL在doc/db/xxx.sql
根据fly_token.sql和fly_user.sql建立两个表fly_token和fly_user
把flylib-site里面的resources/property/jdbc.properties和redis.properties改成自己的数据库和redis配置
下面是一些测试url
注册账号--
http://localhost:20100/passport/register?username=13011111111&password=123456&accountType=1
用手机号作为用户名登录,并获取token--
http://localhost:20100/passport/login?username=13011111111&password=123456&accountType=1
登录成功后,后台会返回token给前端, 前端调用任何需要认证的所有http接口都需要在请求头(headers)带上参数userId和token
例如--
url: http://localhost:20100/account/getInfo
type: post
headers:
userId=5
token=069473bedcc609b0d25d6746c11760e5
你可以用postman来测试,效果图如下
使用了注解@AuthController
同时使用了org.flylib.passport.intercepter.LoginInterceptor
LoginInterceptor的主要代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
logger.info("进入拦截器...........");
Object controller = handlerMethod.getBean();
printParameters(request, controller);
// 访问需要登录的接口那些
// 被@AuthController注解的Controller
boolean present = controller.getClass().isAnnotationPresent(AuthController.class);
if (!present) {
return true;
}
String userIdStr = request.getHeader("userId");
String token = request.getHeader("token");
if (!StringUtils.isEmpty(userIdStr)) {
logger.info("访问需要登录的接口(Authed)...........");
if (StringUtils.isEmpty(token)) {
flushError(response, AuthResponseCode.TOKEN_IS_NULL, AuthResponseCode.TOKEN_IS_NULL_DESC);
return false;
}
Passport passport = loginIntercepterService.getPassport(Long.valueOf(userIdStr));
String storedToken = passport.getToken();
if (StringUtils.isEmpty(storedToken)) {
flushError(response, AuthResponseCode.TOKEN_EXPIRED, AuthResponseCode.TOKEN_EXPIRED_DESC);
return false;
} else if (!storedToken.equals(token)) {
flushError(response, AuthResponseCode.TOKEN_INVALID, AuthResponseCode.TOKEN_INVALID_DESC);
return false;
}
} else {
flushError(response, AuthResponseCode.USER_ID_IS_NULL, AuthResponseCode.USER_ID_IS_NULL_DESC);
return false;
}
// 被@AuthController注解的 结束
}
boolean flag = isPermission(request, response);
logger.info("isPermission:" + flag);
return flag;
}
会去判断客户端当前访问的Controller是否被@AuthController
注解
如果被它注解,了就会进入权限鉴定逻辑。
鉴权逻辑,会提取http headers里面的userId和token值。
后台根据userId去redis查询正确的token(如果没有就去数据库中查询,redis提高了访问数据速度)
如果存储的token跟header里的token相等,则LoginInterceptor给与放行,继续执行MVC里的业务逻辑;
否则,拒绝放行,就是直接把报错的code和codeDes描述以json字符串返回给http客户端。
本来可以放在http parameter(http body)里。 因为http参数里面可能有很多用户自己的参数
都放在一起会比较混乱。 比较规范的做法是把这些权限认证之类的放在请求头headers里
http客户端,需要存放在cookie中,每次请求的时候,需要从cookie中读取;
Android客户端,可以存放在SharedPreferences里面;
有代码改进优化的建议的统一在Issues里面提
加群讨论