SpringSecurity认证流程学习(一)

<!--SpringSecurity依赖--> 
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-security</artifactId>
</dependency>

- SpringSecurity默认环境中,拦截所有请求,要求必须认证(登录)后才能访问。
- 默认环境中,用户名是user,每次启动的时候,动态生成一个长度128字节的密码。在控制台输出。
- 默认环境中,提供登录页面的请求地址是/login, 请求方式是GET
- 默认环境中,处理登录请求的地址是/login, 请求方式是POST

# 静态用户,一般只用在内部网络认证中使用。如:内部服务器1访问内部服务器2.
spring:
    security:
        user:
            name: monologue #通过配置文件,设置静态用户名
            password: monologue #通过配置文件,设置静态登录密码
/**
 * Created by Monologue_zsj on 2021/4/14 14:24
 * Author:小脸儿红扑扑
 * Description:SpringSecurity需要使用的服务对象,
 *  SpringSecurity提供了一个接口,命名为UserDetailsService
 *  其中定义了唯一的认证方法,命名为:loadUserByUsername
 *  方法代表,用户认证。
 *  抛出异常,UsernameNotFoundException,用户查询失败的时候,抛出。
 *  用户查询,是只用用户名作为条件,查询用户。
 * SpringSecurity内部自动匹配密码是否正确的时候,一定要进行加密及解密的处理。
 *      要求Spring容器中,必须存在一个PasswordEncoder实现类对象。
 *      对象中,提供加密逻辑和解密逻辑。解密逻辑不是要求反向计算明文,而是要求实现实现明文和密文的验证。
 *      解密验证可以,加密验证也可以。
 */
@Component
public class SpringSecurityConfiguration implements UserDetailsService {

    @Autowired
    private UserService userService;
    /**
     *
     * @param username 用户登录的用户名
     * @return
     * @throws UsernameNotFoundException    当用户查询失败的时候,抛出的异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = userService.login(username);
        if (user == null) {
            //查询失败,用户名不存在。抛出异常。
            throw new UsernameNotFoundException("用户名错误...");
        }
        /**
         * 查询成功,用户存储。需要匹配用户名密码是否正确。
         * 匹配密码,是由SpringSecurity内部逻辑自动完成。只需要把查询的用户名正确密码返回即可。
         * 返回结果,是UserDetails类型的实现类。可以自定义,可以使用Security框架。
         * security框架提供的UserDetails接口实现类型User,构造的时候,需要提供3个参数,或者7个参数。
         *      3参数构造:
         *              用户身份(用户名)、用户正确密码(数据库中的密码),权限集合(Collection集合)
         *      AuthorityUtils - 工具类,可以通过字符串,创建权限集合。
         */
        org.springframework.security.core.userdetails.User result
                = new org.springframework.security.core.userdetails.User(
                        username,
                        user.getPassword(),
                        AuthorityUtils.createAuthorityList());
        return result;
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 16:36
 * Author:小脸儿红扑扑
 * 第二步:
 * Description:凭证匹配器,专门用于做认证流程的凭证校验使用的类型。
 *      其中有两个核心方法:
 *          1、encode    -   把明文加密成密文密码
 *          2、matches   -   校验明文和密文是否匹配
 */
public class SimplePasswordEncoder implements PasswordEncoder {

    /**
     *  加密逻辑
     * @param rawPassword   明文字符串,就是页面收集的源密码
     * @return
     */
    @Override
    public String encode(CharSequence rawPassword) {
        System.out.println("加密逻辑执行...");
        return new String(rawPassword.toString());
    }

    /**
     * 匹配逻辑
     * @param rawPassword   明文,页面收集的密码
     * @param encodedPassword   密文,存储在数据库中的密码
     * @return
     */
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        System.out.println("密码匹配逻辑执行...");
        return encodedPassword.equals(encode(rawPassword));
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 17:04
 * Author:小脸儿红扑扑
 * Description:MD5加密Encoder,替换上面的SimplePasswordEncoder
 */
public class MyMD5PasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {

        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] tmp = digest.digest(rawPassword.toString().getBytes());
            return toHexString(tmp);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(encode(rawPassword));
    }

    private String toHexString(byte [] tmp) {
        StringBuilder builder = new StringBuilder();
        for (byte b : tmp) {
            String string = Integer.toHexString(b & 0xFF);
            if (string.length() == 1) {
                builder.append("0");
            }
            builder.append(string);
        }
        eturn builder.toString();
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 16:54
 * Author:小脸儿红扑扑
 * Description:加密工具
 */
public class TestMD5 {
    public static void main(String[] args) {
        /*强散列加密*/
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        System.out.println("admin - " + encoder.encode("admin"));
        System.out.println("admin1 - " + encoder.encode("admin1"));
        System.out.println("admin2 - " + encoder.encode("admin2"));
    }

    /*MD5加密*/
    public static void testMd5() throws NoSuchAlgorithmException {
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] digest1 = md5.digest("admin".getBytes());
        byte[] digest2 = md5.digest("admin1".getBytes());
        byte[] digest3 = md5.digest("admin2".getBytes());

        System.out.println("admin - " + toString(digest1));
        System.out.println("admin1 - " + toString(digest2));
        System.out.println("admin2 - " + toString(digest3));
    }
    private static String toString(byte [] tmp) {
        StringBuilder builder = new StringBuilder();
        for (byte b : tmp) {
            String string = Integer.toHexString(b & 0xFF);
            if (string.length() == 1) {
                builder.append("0");
            }
            builder.append(string);
        }
        return builder.toString();
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 16:49
 * Author:小脸儿红扑扑
 * 第三步:
 * Description:
 */
@Configuration
public class MySpringSecurityConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        
//        return new SimplePasswordEncoder();
//        return new MyMD5PasswordEncoder();
        return new BCryptPasswordEncoder();
    }
}

/**
 * Created by Monologue_zsj on 2021/4/14 16:49
 * Author:小脸儿红扑扑
 * 第三步:
 * Description:
 */
@Configuration
public class MySpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 重写父类中的配置逻辑
     * 如果调用了super.configure,则使用父类型的默认流程配置
     * 需要自定义,则删除super.configure方法的调用
     * 当提供自定义配置,删除super.configure方法的时候,所有的默认流程全部清空
     *      http.formLogin.loginProcessingUrl(String path);
     *          用户登录请求地址,就是处理用户登录逻辑的地址。
     *          SpringSecurity提供的处理登录请求控制器,是path监听软编码的。可以通过此方法动态配置监听地址。
     *          只要配置地址和页面的请求地址一致,即可完成登录逻辑。
     *          登录成功,默认返回的页面是 '/',如果有前置请求,则默认返回前置地址
     *          登录失败,默认返回的页面是 http.formLogin().loginPage(String path) 方法参数?error
     *     http.formLogin().defaultSuccessUrl(String path, boolean alwaysUse)
     *          设置登录成功后,响应重定向地址,SpringSecurity要求,必须传递绝对路径,
     *          如果传递的是相对路径,则相对于当前服务器的根开始寻址。http://localhost:port/
     *          参数alwaysUse可以省略。默认是false。配置为false的时候,通常重定向失效。
     *
     * 登录请求参数命名:
     *      默认规则是:用户名必须是username,密码必须是password
     *      此逻辑由UsernamePasswordAuthenticationFilter决定。
     *
     * @param http  基于Http协议的Security配置对象,包含所有的SpringSecurity相关逻辑配置
     * @throws Exception    当配置出错的时候抛出
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //配置登录请求相关内容
        http.formLogin()
                //.usernameParameter("user")    //设置请求参数中,用户名参数名称   默认username
                //.passwordParameter("pwd")     //设置请求参数中,密码参数名称     默认password
                .loginPage("/toLogin")     //当用户未登录的时候,跳转的登录页面地址是什么,默认/login
                .loginProcessingUrl("/login")  //用户登录逻辑请求地址是什么,默认 /login
                .successHandler(new MyAuthenticationSuccessHandler("/toMain", true))  //使用自定义请求处理控制逻辑(Handler)实现登录成功后的请求处理,需要AuthenticationSuccessHandler类型的对象
                .failureHandler(new MyAuthenticationFailureHandler("/failure", true));  //自定义认证失败后的处理逻辑
                //.failureUrl("/failure");    //登录失败后,重定向的位置。记得提供权限配置校验
                //.failureForwardUrl("failure");  //登录失败后,请求转发的位置。Security请求转发使用POST请求
                //.defaultSuccessUrl("/toMain", true);  //用户登录成功后,响应重定向到的位置,GET请求
                //.successForwardUrl("/toMain");     //用户登录成功后,请求转发到的位置。Security请求转发使用POST请求

        //配置权限校验,如:什么地址 必须认证后才能访问,什么地址可以不用认证就可以访问
        //权限校验的配置,是线性的。从开始的配置位置开始校验,成功立刻返回。
        //校验匹配失败,继续后续的逻辑校验
        http.authorizeRequests()
                .antMatchers("/toLogin", "/failure").permitAll()    //toLogin请求地址,可以随便访问
                .anyRequest().authenticated();  //任意的请求必须认证后才可以访问

        //关闭CSRF安全协议,关闭是为了完整流程的可用
        http.csrf().disable();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {

//        return new SimplePasswordEncoder();
//        return new MyMD5PasswordEncoder();
        return new BCryptPasswordEncoder();
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 21:14
 * Author:小脸儿红扑扑
 * Description:自定义登录成功后的处理器
 *  转发或者重定向,由代码逻辑实现
 */
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private String url;
    private boolean isRedirect;

    public MyAuthenticationSuccessHandler(String url, boolean isRedirect) {
        this.url = url;
        this.isRedirect = isRedirect;
    }

    /**
     * @param request   请求对象    request.getRequestDispatcher().forward()
     * @param response  响应对象    response.sendRedirect();
     * @param authentication    用户认证(登录)成功后的对象。
     *                              其中包含用户名和权限集合,
     *                              内容是自定义的UserDerails实现的方法值对象提供的
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        if (isRedirect) {
            response.sendRedirect(url);
        }else {
            request.getRequestDispatcher(url).forward(request, response);
        }
    }

    /*getter和setter方法不是必要的*/
    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isRedirect() {
        return isRedirect;
    }

    public void setRedirect(boolean redirect) {
        isRedirect = redirect;
    }
}
/**
 * Created by Monologue_zsj on 2021/4/14 22:21
 * Author:小脸儿红扑扑
 * Description:自定义登录失败后的处理器
 */
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private String url;
    private Boolean isRedirect;

    public MyAuthenticationFailureHandler(String url, Boolean isRedirect) {
        this.url = url;
        this.isRedirect = isRedirect;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {

        if (isRedirect) {
            response.sendRedirect(url);
        }else {
            request.getRequestDispatcher(url).forward(request, response);
        }
    }


    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Boolean getRedirect() {
        return isRedirect;
    }

    public void setRedirect(Boolean redirect) {
        isRedirect = redirect;
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值