jwt只是一个生成token的机制,而Spring Security、Shiro这两个框架是用来后台做权限认证,管理,筛选的框架
JWT又名Json Web Token,基于数字签名,定义了一个紧凑、字包含的方式,用于json在各方之间安全传输信息。
一般用于授权认证和数据交换:
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 token,然后这个token就是用户凭证了,通常token有失效时间(通常都放在redis里面保存)
带着token访问受保护的api接口的时候,一般都会把token放在Authorization header中Bearer schema的值里。好处是这样不会使用cookie。
因为http是无状态的,无法记录请求方,于是大家都会发送cookie到服务器端,或者是在服务器端上记录sessionId
问题点:
JWT的好处:
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(控制反转),DI( 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它是一个轻量级的安全框架,它确保基于Spring的应用程序提供身份验证和授权支持。它与Spring MVC有很好地集成,并配备了流行的安全算法实现捆绑在一起。安全主要包括两个操作“认证”与“验证”(有时候也会叫做权限控制)。“认证”是为用户建立一个其声明的角色的过程,这个角色可以一个用户、一个设备或者一个系统。“验证”指的是一个用户在你的应用中能够执行某个操作。在到达授权判断之前,角色已经在身份认证过程中建立了。
它的设计是基于框架内大范围的依赖的,可以被划分为以下几块。
工作流程
在WEB项目中,它基于fiter。Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验
Spring Security维护了一个fiter chain,按照特定的顺序执行(filter之间有以来)
1.ChannelProcessingFilter
核心组件
SecurityContext:安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中,SecurityContext的接口定义
//存取安全上下文
public interface SecurityContext extends Serializable {
/**
* Obtains the currently authenticated principal, or an authentication request token.
*
* @return the Authentication
or null
if no authentication
* information is available
*/
Authentication getAuthentication();
/**
* Changes the currently authenticated principal, or removes the authentication
* information.
*
* @param authentication the new Authentication
token, or
* null
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,根据需要的功能在配置中配置。
流程
Apache Shiro是Java的一个安全框架,在轻量级的程序应用更广泛,简化了Spring security的功能,提供了处理身份认证,授权,企业会话管理和加密的功能。
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。
Spring security 与apache shiro 差别:
a)shiro配置更加容易理解,容易上手;security配置相对比较难懂。
b)在spring的环境下,security整合性更好。Shiro对很多其他的框架兼容性更好,号称是无缝集成。
c)shiro 不仅仅可以使用在web中,它可以工作在任何应用环境中。 d)在集群会话时Shiro最重要的一个好处或许就是它的会话是独立于容器的。 e)Shiro提供的密码加密使用起来非常方便。
d)控制精度: 注解方式控制权限只能是在方法上控制,无法控制类级别访问。