如果你是刚接触springsecurity可以跳过本文章,本文章讲的是springsecurity对于前后端分离项目权限管理的一些点
首先是UserDetailsService
,这是springsecurity提供的一个接口,可以写一个类实现这个接口,然后从数据中获取用户信息,而不是使用springsecurity给定的一个用户进行controller访问。
UserDetailsService
接口只需要重写一个方法(loadUserByUsername
)该方法返回的是UserDetails
类型,传进来的参数是登陆页面的username
,springsecurity提供了一个User
类作为UserDetailsService
实现类,如果不满足需要也可以自定义一个。
我这边自定义了一个UserDetailsService实现类:
使用mybatis-plus对数据库表进行操作,看到
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException
这个方法,可能会产生,是不是没有对密码进行验证的想法,我经过一系列debug之后找到了,对密码进行验证的地方。
AbstractUserDetailsAuthenticationProvider
类里面,圈住的这个方法里面对密码进行了检验
进入这个方法会找到这里
查看控制台的信息
登录传过来的用户名的信息被封装成一个Authentication
对象,注意!!!
在这种情况下principal确确实实是前端传过来的username信息,但是如果自定义实现了UserDetails
并使用了,那么principal就会是UserDetails的实现类的一个对象(这个点后面还会提到)
在这个matches if判断中,使用的是数据库表用户的密码和前端传过来的密码进行对比,如果匹配则认证成功,否则抛出异常
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));
数据库表中一般是不会存储密码明文的,而且如果要使用其他加密方式,如不可逆的MD5
对密码进行加密,这个时候就需要自定义PasswordEncoder
实现类了。
实现PasswordEncoder
接口需要重写这三个方法,因为在springsecurity执行时会调用这三个方法对密码进行一些处理。
由于springsecurity对MD5加密进行了封装,可以简洁的使用:
编写一个MD5工具类
MD5.java
package com.sixteen.utils.utils;
import org.springframework.util.DigestUtils;
public final class MD5 {
public static String encrypt(String strSrc){
return DigestUtils.md5DigestAsHex(strSrc.getBytes());
}
public static void main(String[] args) {
System.out.println(MD5.encrypt("111111"));
}
}
以上就是自定义的PasswordEncoder;
接下来介绍自定义UsernamePasswordAuthenticationFilter
springsecurity整体就是一个filter chain 是由很多个过滤器实现的
而这个过滤器就是对用户进行认证的。
实现UsernamePasswordAuthenticationFilter
需要重写三个方法
查阅了资料才知道,Spring Security默认居然不能获取request中的Json数据,所以进行前后端分离的登录时,重写attemptAuthentication
方法就需要一些别的处理,通过fastjson的ObjectMapper的方法readValue对HTTPServletRequest进行处理,使用的是public <T> T readValue(InputStream src, Class<T> valueType)
这个参数的readValue方法
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
return super.attemptAuthentication(request, response);
}
attemptAuthentication
方法需要这样写,User
类是自己定义的用户类,至少需要两个属性,username和password
try {
/**
* User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
* 通过这个方式获取前端发送的异步请求传过来的json数据,并转换为自定义的User的对象
*
*/
User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
new ArrayList<>()
));
} catch (IOException e) {
e.printStackTrace();
// 抛出一个全局异常
throw new RuntimeException();
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,FilterChain chain, Authentication authResult)
throws IOException, ServletException {
/**
* 在username+password情况下getPrincipal()返回的是username
* 有userDetails之后返回的就是userDetails的实现类
*/
/**
* 认证成功后跳转到此方法,
* 1. 从Authentication authResult中获取用户名,用户权限列表,
* 2. 利用tokenManager生成token
* 3. 再用redisTemplate把 username:authorities 的键值对,存入redis (即存入 用户名:权限列表 的键值对)
* 4. 再把生成的token返回给前端
*/
SecurityUser user = (SecurityUser) authResult.getPrincipal();
}
对于successfulAuthentication
方法中的参数 Authentication authResult
通过authResult.getPrincipal()
可以得到一个对象,该对象
在username+password情况下getPrincipal()返回的是username 有userDetails之后返回的就是userDetails的实现类
以上就是我觉得springsecurity比较重要的知识,如有写的不足或者可能有错误的地方,麻烦各位大佬指点一下。