一般来讲,对于RESTful API都会有认证(Authentication)和授权(Authorization)过程,保证API的安全性。
Authentication指的是确定这个用户的身份,Authorization是确定该用户拥有什么操作权限。
认证方式一般有三种
Basic Authentication
这种方式是直接将用户名和密码放到Header中,使用Authorization: Basic Zm9vOmJhcg==
,使用最简单但是最不安全。
TOKEN认证
这种方式也是再HTTP头中,使用Authorization: Bearer <token>
,使用最广泛的TOKEN是JWT,通过签名过的TOKEN。
OAuth2.0
这种方式安全等级最高,但是也是最复杂的。如果不是大型API平台或者需要给第三方APP使用的,没必要整这么复杂。
一般项目中的RESTful API使用JWT来做认证就足够了。
关于JWT的介绍请参考我的另一篇文章:【安全贴士】JWT介绍和安全防范 | 飞污熊博客
SpringBoot集成JWT
简要的说明下我们为什么要用JWT,因为我们要实现完全的前后端分离,所以不可能使用session,cookie的方式进行鉴权, 所以JWT就被派上了用场,可以通过一个加密密钥来进行前后端的鉴权。
程序逻辑:
我们POST用户名与密码到/login进行登入,如果成功返回一个加密token,失败的话直接返回401错误。之后用户访问每一个需要权限的网址请求必须在header中添加Authorization字段,例如Authorization: token,token为密钥。后台会进行token的校验,如果不通过直接返回401。
这里我讲一下如何在SpringBoot中使用JWT来做接口权限认证,安全框架依旧使用Shiro,JWT的实现使用jjwt
添加Maven依赖
创建用户Service
这个在shiro一节讲过如果创建角色权限表,添加用户Service来执行查找用户操作,这里就不多讲具体实现了,只列出关键代码:
用户信息类:
JWT工具类
我们写一个简单的JWT加密,校验工具,并且使用用户自己的密码充当加密密钥, 这样保证了token 即使被他人截获也无法破解。并且我们在token中附带了username信息,并且设置密钥5分钟就会过期。
编写登录接口
为了让用户登录的时候获取到正确的JWT Token,需要实现登录接口,这里我编写一个LoginController.java
:
注意上面登录的时候,我会从数据库中把这个用户取出来,密码加盐算MD5值比较,通过之后再用密码作为密钥来签名生成JWT。
编写RESTful接口
先编写一个通用的接口返回类:
通过SpringMVC实现RESTful接口,这里我只写一个示例方法:
自定义异常
为了实现我自己能够手动抛出异常,我自己写了一个UnauthorizedException.java
处理框架异常
之前说过restful要统一返回的格式,所以我们也要全局处理Spring Boot的抛出异常。利用@RestControllerAdvice能很好的实现。 注意这个统一异常处理器只对认证过的用户调用接口中的异常有作用,对AuthenticationException没有用
配置Shiro
大家可以先看下官方的Spring-Shiro整合教程,有个初步的了解。 不过既然我们用了SpringBoot,那我们肯定要争取零配置文件。
实现JWTToken
JWTToken差不多就是Shiro用户名密码的载体。因为我们是前后端分离,服务器无需保存用户状态,所以不需要RememberMe这类功能, 我们简单的实现下AuthenticationToken接口即可。因为token自己已经包含了用户名等信息,所以这里我就弄了一个字段。 如果你喜欢钻研,可以看看官方的UsernamePasswordToken是如何实现的。
实现Realm
realm的用于处理用户是否合法的这一块,需要我们自己实现。
在doGetAuthenticationInfo
中用户可以自定义抛出很多异常,详情见文档。
重写Filter
所有的请求都会先经过Filter,所以我们继承官方的BasicHttpAuthenticationFilter
,并且重写鉴权的方法, 另外通过重写preHandle,实现跨越访问。
代码的执行流程preHandle->isAccessAllowed->isLoginAttempt->executeLogin
编写ShiroConfig配置类
这里我还增加了EhCache缓存管理支持,不需要每次都调用数据库做授权。
里面URL规则自己参考文档Apache Shiro Web Support | Apache Shiro,这个在shiro那篇说的很清楚了。
运行验证
最后是将代码跑起来验证这一切是否正常。
启动SpringBoot后,先通过POST请求登录拿到token
然后在调用入网接口的时候在header中带上这个token认证:
如果token认证不正确会报异常:
如果使用普通用户登录,认证正确但是授权访问接口失败,会返回如下的未授权结果: