JWT和Spring Security、Shiro的区别
jwt只是一个生成token的机制,而Spring Security、Shiro这两个框架是用来后台做权限认证,管理,筛选的框架
JWT
jwt是什么?
JWT又名Json Web Token,基于数字签名,定义了一个紧凑、字包含的方式,用于json在各方之间安全传输信息。
使用场景
一般用于授权认证和数据交换:
- Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用
- Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
JWT架构图
JWT由什么组成的
JWT由Header、Payload、Signature组成,它们之间用 . 连接。例如
111(Header).222(Payload).333(Signature)
由token的类型和算法名称组成json,然后在对这个json进行BASE64编码
json格式如下;
{
"alg":"RSA",
"type":"JWT"
}
JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
Public claims: 可以随意定义。
Private claims: 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
{
"admin":"true",
"name":"zzz"
}
payload是对上面json进行base64编码,然后放入jwt的第二部分
为了得到签名,必须要具备前两个条件(base64 编码过后的header、payload),还需要一个密钥,签名算法使用的header中的指定的算法,然后对他们签名
RSA(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
官网解释图:
JWT是如何工作的
用户从客户端登录成功后,后端会返回一个jwt token,然后这个token就是用户凭证了,通常token有失效时间(通常都放在redis里面保存)
带着token访问受保护的api接口的时候,一般都会把token放在Authorization header中Bearer schema的值里。好处是这样不会使用cookie。
流程如下:
- 客户端访问授权服务器,请求授权;
- 返回一个token
- 然后用token访问受保护的资源
postman截图
和老版本的session相比的优点
因为http是无状态的,无法记录请求方,于是大家都会发送cookie到服务器端,或者是在服务器端上记录sessionId
问题点:
- 服务器端记录大量的sessionid,对内存开销有影响
- 跨域问题
- 安全性
JWT的好处:
- token存放在客户端,减轻服务器端的负担,可以让负载均衡器将用户传递到任意服务器
- 每次请求都需要带上token,没有会话,安全性大
JWT的流程使用
- 用户携带用户名和密码请求访问
- 服务器校验用户凭据
- 应用提供一个token给客户端
- 客户端存储token,并且在随后的每一次请求中都带着它
- 服务器校验token并返回数据
Spring Security
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(控制反转),DI( 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和授权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一起。安全主要包括两个操作“认证”与“验证”(有时候也会叫做权限控制)。“认证”是为用户建立一个其声明的角色的过程,这个角色可以一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中能够执行某个操作。在到达授权判断之前,角色已经在身份认证过程中建立了。
它的设计是基于框架内大范围的依赖的,可以被划分为以下几块。
- Web/Http 安全:这是最复杂的部分。通过建立 filter 和相关的 service bean 来实现框架的认证机制。当访问受保护的 URL 时会将用户引入登录界面或者是错误提示界面。
- 业务对象或者方法的安全:控制方法访问权限的。
- AuthenticationManager:处理来自于框架其他部分的认证请求。
- AccessDecisionManager:为 Web 或方法的安全提供访问决策。会注册一个默认的,但是我们也可以通过普通 bean 注册的方式使用自定义的 AccessDecisionManager。
- AuthenticationProvider:AuthenticationManager 是通过它来认证用户的。
- UserDetailsService:跟 AuthenticationProvider 关系密切,用来获取用户信息的
工作流程
在WEB项目中,它基于fiter。Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验
Spring Security维护了一个fiter chain,按照特定的顺序执行(filter之间有以来)
1.ChannelProcessingFilter
核心组件
- SecurityContext
- SecurityContextHolder
- Authentication
- Userdetails
- AuthenticationManager,
SecurityContext:安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中,SecurityContext的接口定义
//存取安全上下文
public interface SecurityContext extends Serializable {
/**
* Obtains the currently authenticated principal, or an authentication request token.
*
* @return the <code>Authentication</code> or <code>null</code> if no authentication
* information is available
*/
Authentication getAuthentication();
/**
* Changes the currently authenticated principal, or removes the authentication
* information.
*
* @param authentication the new <code>Authentication</code> token, or
* <code>null</code> if no further authentication information should be stored
*/
void setAuthentication(Authentication authentication);
}
SecurityContextHolder:
维持SecurityContext的实例,该上下文将上下文存储为HTTP请求之间的HttpSession属性。它会为每个请求恢复上下文SecurityContextHolder,并且最重要的是,在请求完成时清除SecurityContextHolder。SecurityContextHolder是一个类,他的功能方法都是静态的。它可以自定义SecurityContext的jvm存储策略,里面实现方法多用于存储当前认证信息
/**
* jvm策略
* MODE_THREADLOCAL(默认jvm设置。存在线程中)
* MODE_INHERITABLETHREADLOCAL(也是存在线程中,但是子线程可以获得浮现出)
* MODE_GLOBAL:SecurityContext 在所有线程中都相同
**/
//和session存储sessinid一样
SecurityContextHolder.getContext().setAuthentication(token);
Authentication:
Authentication是一个接口,一般在Spring Security中表示登录用户是谁。
public interface Authentication extends Principal, Serializable {
//获取用户的角色信息
Collection<? extends GrantedAuthority> getAuthorities();
//获取证明用户认证的信息,通常情况下获取到的是密码等信息
Object getCredentials();
//获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)
Object getDetails()
//获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails ;
Object getPrincipal();(UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。
//获取当前 Authentication 是否已认证。
boolean isAuthenticated();
//设置当前 Authentication 是否已认证(true or false)
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
AuthenticationManager:
是一个校验Authentication的接口,如果失败会抛出一个抽象类AuthenticationException的异常,因为不能实例化抽象类,所以无法抛出AuthenticationException异常,所以一般都是实现类DisabledException,LockedException,BadCredentialsException(密码错误的异常)
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
UserDetails:
存储用户信息;
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();//获取用户的角色信息
String getPassword();
String getUsername();
boolean isAccountNonExpired();//是否过期
boolean isAccountNonLocked();//是否被锁定
boolean isCredentialsNonExpired();//密码是否失效
boolean isEnabled();//账号是否可用
}
UserDetailsService:
UserDetailsService是一个获取UserDetails的接口,只有一个loadUserByUsername(final String login)方法。
实际开发中
自定义一个实现类去实现UserDetailsService接口,并且实现loadUserByUsername这个方法,可以通过查询数据库/缓存来获取用户信息,然后组成一个UserDetails并返回,如果没有查询数据,那就抛出异常(org.springframework.security.core.userdetails.UsernameNotFoundException)去告诉spring security去处理。
工作原理及应用范围
Spring security 是基于filter的,filter按照顺序去执行,根据filterChain去调用下一个filter,Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验。Spring Security有一个filterChain去管理filter,根据需要的功能在配置中配置。
流程
- spring security 的核心是基于filter
- 入口filter是springSecurityFilterChain(它会被DelegatingFilterProxy委托来执行过滤任务)
- springSecurityFilterChain实际上是FilterChainProxy (一个filter)
- FilterChainProxy里边有一个SecurityFilterChain集合,doFIlter的时候会从其中取
Shiro
Apache Shiro是Java的一个安全框架,在轻量级的程序应用更广泛,简化了Spring security的功能,提供了处理身份认证,授权,企业会话管理和加密的功能。
应用领域
- 验证用户
- 对用户执行访问控制,如:
判断用户是否拥有角色admin。
判断用户是否拥有访问的权限 - 在任何环境下使用 Session API。例如CS程序。
- 可以使用多个用户数据源。例如一个是oracle用户库,另外一个是mysql用户库。
- 单点登录(SSO)功能。
- “Remember Me”服务 ,类似购物车的功能,shiro官方建议开启
核心领域:
- 身份验证Authentication(重要模块)
- 授权Authorization(重要模块)
- 会话管理Session Management(重要模块)
- 加密Cryptography(重要模块)
- web support:Web支持,可以非常容易的集成到Web环境(次要)
- Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;(次要)
- Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;(次要)
- Testing:提供测试支持;(次要)
- Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;(次要)
- Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了(次要)
核心组件:
- Subject 主体
- SecurityManager 安全管理器 (Subject 的管理者)
- Realms 域
Subject
代表了与当前应用交互的任何东西,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
SecurityManager
SecurityManager 是 Shiro的核心,初始化时协调各个模块运行。然而,一 旦 SecurityManager协调完毕,SecurityManager 会被单独留下,且我 们只需要去操作Subject即可,无需操SecurityManager 。 但是我们 得知道,当我们正与一个 Subject 进行交互时,实质上是 SecurityManager在处理 Subject 安全操作
Shiro
Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
常用shiro 注解
@RequiresAuthentication
验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。
@ RequiresUser
验证用户是否被记忆,user有两种含义: 一种是成功登录的(subject.isAuthenticated() 结果为true); 另外一种是被记忆的( subject.isRemembered()结果为true)。
@ RequiresGuest
验证是否是一个guest的请求,与@ RequiresUser完全相反。 换言之,RequiresUser == ! RequiresGuest 。 此时subject.getPrincipal() 结果为null.
@ RequiresRoles
例如:@RequiresRoles(“aRoleName”);void someMethod(); 如果subject中有aRoleName角色才可以访问方法someMethod。如果没有这个 权限则会抛出异常AuthorizationException。
@RequiresPermissions
例如: @RequiresPermissions( {“file:read”, “write:aFile.txt”} ) void someMethod();要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法someMethod()。否则抛出异常AuthorizationException。
shiro和spring security差别
Spring security 与apache shiro 差别:
a)shiro配置更加容易理解,容易上手;security配置相对比较难懂。
b)在spring的环境下,security整合性更好。Shiro对很多其他的框架兼容性更好,号称是无缝集成。
c)shiro 不仅仅可以使用在web中,它可以工作在任何应用环境中。 d)在集群会话时Shiro最重要的一个好处或许就是它的会话是独立于容器的。 e)Shiro提供的密码加密使用起来非常方便。
d)控制精度: 注解方式控制权限只能是在方法上控制,无法控制类级别访问。