1.shiro和spring-security区别
首先Shiro
较之Spring Security
,Shiro
在保持强大功能的同时,还在简单性和灵活性方面拥有巨大优势。Shiro
是一个强大而灵活的开源安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加密。
Spring Security
除了不能脱离Spring
,Shiro
的功能它都有。Spring Security
对Spring
结合较好,如果项目用的springmvc
,使用起来很方便。
我们公司的登录认证主要使用了Shiro
,实现了登录认证以及oauth2认证。提供了接口供不同个性化登录实现,如:
IdentityBuilder
:构建登录身份,控制登录流程UserService
:查询用户真实信息CredentialsMatcher
:校验登录身份和真实信息NamedAuthenticationListener
:登录成功、失败的后处理
参照这套认证体系,我们采用Spring Security
来重构以下。
2.jwt和session的选择
基于session和基于jwt的方式的主要区别就是用户的状态保存的位置,session是保存在服务端的,而jwt是保存在客户端的。jwt的优点就不说了,主要说说缺点
- 由于jwt的payload是使用base64编码的,并没有加密,因此jwt中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全。
- jwt太长。由于是无状态使用JWT,所有的数据都被放到JWT里,如果还要进行一些数据交换,那载荷会更大,经过编码之后导致jwt非常长,cookie的限制大小一般是4k,cookie很可能放不下,所以jwt一般放在local storage里面。并且用户在系统中的每一次http请求都会把jwt携带在Header里面,http请求的Header可能比Body还要大。而sessionId只是很短的一个字符串,因此使用jwt的http请求比使用session的开销大得多。
- 无状态是jwt的特点,但也导致了这个问题,jwt是一次性的。想修改里面的内容,就必须签发一个新的jwt。
为了安全性我们也是选择session来保存用户状态,而我们公司的系统经常要在服务端根据session进行权限控制,而jwt主要用在移动端的无状态场景。
3.spring-security权限控制实现
3.1 AuthenticationBuilder对标IdentityBuilder
Spring Security
用一个类专门负责接手前端数据构建登录身份,这个类时UsernamePasswordAuthenticationFilter
,但是这个类只能接受url请求中的参数,对于请求参数不在url中的则没法解析。所以我们需要个性化这个类让他能够Request PayLoad
中的数据。
/**
* {@link UsernamePasswordAuthenticationFilter}
*/
public class FrameUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final AuthenticationBuilder authenticationBuilder;
public FrameUsernamePasswordAuthenticationFilter(AuthenticationBuilder authenticationBuilder) {
super(new AntPathRequestMatcher("/api/login", "POST"));
this.authenticationBuilder = authenticationBuilder;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
Authentication authRequest = authenticationBuilder.build(request, response);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
复制代码
另外我们在封装一个AuthenticationBuilder
接口用于个性化实现,并提供默认实现FrameAuthenticationBuilder
public class FrameAuthenticationBuilder implements AuthenticationBuilder {
@Override
public Authentication build(HttpServletRequest request, HttpServletResponse response) {
LoginVO loginVO = JSON.parseObject(getRequestPayload(request), LoginVO.class);
return new UsernamePasswordAuthenticationToken(
loginVO.getUsername(), loginVO.getPassword());
}
public String getRequestPayload(HttpServletRequest request) {
try {
return IOUtils.toString(request.getReader());
} catch (IOException ex) {
ex.printStackTrace();
}
return "";
}
}
复制代码
3.2 UserDetailsService对标UserService
Spring Security
有个类专门负责通过username
查找用户信息的接口,这个接口就是UserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//可以从数据库里根据username去除用户信息
}
复制代码
3.3 DaoAuthenticationProvider对标CredentialsMatcher
AbstractUserDetailsAuthenticationProvider
类提供了一个抽象方法additionalAuthenticationChecks
用子类DaoAuthenticationProvider
实现,UserDetails
参数为AuthenticationBuilder
构建的前端参数,UsernamePasswordAuthenticationToken
为UserDetailsService
通过前端传递的username
去数据库中查询到的用户信息,对比两个对象的密码判断是否认证通过。值