开发一个小项目时,针对不同接口要有权限管理的控制,没有动态分配权限那么复杂,主要区分管理员和用户。
于是调研了常用的权限验证框架,主要参考了shiro、spring security、spring OAuth2。
在阅读完这些框架的文档以及尝试搭建后,一点也不想用它们了。
这些框架很成熟,支持缓存、验证各种环节的定制化,但是对于小项目来说,还是有一定理解成本,而且初始化配置起来也挺麻烦的。
对于大多数小项目来说,用不着那么多扩展,其实就是想实现一个简单的登录及权限验证,所以我决定自己实现。
实现思路很简单,主要以下几个考虑点:
- 自定义拦截器,根据URL进行拦截:这点跟框架思路都是一样的,都是基于拦截器实现的;
- 使用Cookie存储token进行验签:我非常不喜欢Spring security中
JdbcDaoImpl
默认的SQL,限制太死了,更倾向于使用客户端无法看出含义的token进行验签; - 缓存及其他扩展:这部分自己写定制化程度更高,比如不想每次都查库,那可以用内存缓存或者Redis也都行,也都不复杂;
好了,下面开始搞。
第一步:定义一个自定义的LoginInterceptor
实现HandlerInterceptor
。
注意,不同版本的spring-mvc
表现不同,比如5.x版本的不要求全部实现接口,有默认实现,根据个人项目中版本情况,我们关键要实现preHandle
方法,即访问逻辑内容之前的拦截。
拦截器定义代码如下:
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { //这里就是验证操作,稍后在这里写代码 return false; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
XML配置如下(也可以使用注解@Bean
):
<mvc:interceptor> <mvc:mapping path="/manage/**"/> <mvc:exclude-mapping path="/login"/> <bean class="com.test.interceptor.LoginInterceptor"/> </mvc:interceptor>
以下是注解的使用方式:
@Configuration public class MyWebMvcConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInteceptor).addPathPatterns("/user/**"); } //这里@Bean,是为了能在Interceptor里用@Autowired等注入其他类,也可以直接在LoginInterceptor上面加@Component @Bean public MpLoginInteceptor loginInteceptor() { return new LoginInteceptor(); } }
第二步:先确定验证方式,我使用Cookie来验证。
由于直接依赖session会导致服务器重启后用户需要重新登录,因此在用户登录成功后,我会给用户生成一个token存到库里,保证通过token能查询到登录的是谁。
如果是网页应用,也可以直接把token设置到cookie里,加上生命周期,确保下次打开浏览器后还能记住上次登录状态,代码如下:
Cookie cookie = new Cookie("token", "token值"); cookie.setMaxAge(expire); cookie.setPath(path); response.addCookie(cookie);
如果非网页应用,可以使用Cookie,也可以在header里设置token,这个自己定就好,区别只是获取token时的代码不同,比如很多小程序喜欢把token放在header里。
第三步:实现preHandler,读取token。
读取token有多种方式,根据自己在登陆时的设置,代码如下:
@Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { String token = null; //如果是直接放在参数里,比如get或者post的参数里 token = httpServletRequest.getParameter("token"); //如果是放在Cookie里,我封装了一个util方法,下面有 token = getCookieValue(httpServletRequest, "token"); //如果是放在了Header里,key是可以改的,自己设置什么名字都行,只要保证设置和读取一致 token = httpServletRequest.getHeader("token"); //下面就是根据token获取用户信息就可以了 //token不存在或根据token查不到用户信息,就是违法的 return false; } public static String getCookieValue(HttpServletRequest request, String key) { Cookie[] cookies = request.getCookies(); if (null == cookies) { return null; } for (Cookie cookie : cookies) { if (key.equals(cookie.getName())) { return cookie.getValue(); } } return null; }
以上基本上就是整个框架了,小项目完全没问题了,在此基础上自己还可以扩展。
比如根据token查询用户信息加一个本地缓存,或者自定义一个权限注解,可以做根据角色更精确的权限控制,也都很简单,后续有需要可以再讨论下。