SpringSecurity是一个非常强大的安全验证框架,它通过一个过滤器链完成了对资源的访问控制。security框架本身提供了多种验证方式,如OA,Session等,但是有些情况下还是无法满足用户的需求,故此security框架也有非常大的扩展性,它允许用户自定义认证流程,下面我们就结合security实现一个jwt认证流程。
拦截登录请求
security框架中通过UsernamePasswordAuthenticationFilter来拦截登录请求,并将登录信息封装为待验证的token对象。由于我们需要自定义认证流程,所以要继承UsernamePasswordAuthenticationFilter的父类实现自己的拦截器来构造token。
public class JwtAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationProcessingFilter() {
//该过滤器会拦截哪些路径和类型的请求
super(new AntPathRequestMatcher("/Jwtlogin", "POST"));
}
/**
* 该方法是实现将用户登录信息从request提取,并封装成一个未认证的token传给AuthenticationManager处理
*
* @param httpServletRequest
* @param httpServletResponse
* @return
* @throws AuthenticationException
* @throws IOException
* @throws ServletException
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
try {
String username=httpServletRequest.getParameter("username");
String password=httpServletRequest.getParameter("password");
JwtToken jwtToken=new JwtToken(username,password);
//将请求的会话id,IP地址保存在token中
jwtToken.setDetails(new WebAuthenticationDetails(httpServletRequest));
//调用认证管理器对token进行认证
Authentication authentication=this.getAuthenticationManager().authenticate(jwtToken);
return authentication;
}catch (Exception e)
{
throw new BadCredentialsException("校验凭证失败,"+e.getMessage());
}
}
}
实现token
由于JwtAuthenticationProcessingFilter会调用AuthenticationManager进行认证,而AuthenticationManager内部存在多个认证方式,它通过token的类型来选择其中一种进行验证,因此我们需要实现一个自己的token。
public class JwtToken extends AbstractAuthenticationToken {
private Object Principal;
private Object Credential;
/**
* 构造等待认证的token
* @param principal
* @param credential
*/
public JwtToken(Object principal, Object credential) {
super(null);
Principal = principal;
Credential = credential;
//设置token状态为未认证
setAuthenticated(false);
}
/**
* 构造已认证的token
* @param authorities
* @param principal
* @param credential
*/
public JwtToken(Collection<? extends GrantedAuthority> authorities, Object principal, Object credential) {
//保存权限
super(authorities);
Principal = principal;
Credential = credential;
//设置token状态为已认证
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.Credential;
}
@Override
public Object getPrincipal() {
return this.Principal;
}
}
实现认证方式
Security的认证方式都实现AuthenticationProvider接口,所以我们只需要这个接口就可以自定义认证方式了,这个接口有authenticate和supports两个方法,authenticate接收一个待认证的token作为参数,认证成功则重新构造一个已认证的token并返回。supports方法用于判断是否该认证方式支持哪种token。
public class JwtAuthProvider implements AuthenticationProvider{
@Resource(name = "UserDetailServiceImpl")
private UserDetailsService userDetailsService;
@Autowired
private JwtPasswordEncoder encoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JwtToken jwtToken= (JwtToken) authentication;
UserDetails details=userDetailsService.loadUserByUsername(jwtToken.getName());
AuthenticationCheck(details,jwtToken);
SecurityCheck(details);
JwtToken token=new JwtToken(details.getAuthorities(),details,jwtToken.getCredentials())