Bootstrap

springsecurity UsernamePasswordAuthenticationFilter PasswordEncoder

如果你是刚接触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比较重要的知识,如有写的不足或者可能有错误的地方,麻烦各位大佬指点一下。

;