SpringSecurity+Redis认证过程总结

由于今天用Security进行权限管理的时候出现了一些Bug,特此发这篇博客来补习一下对SpringSecurity的理解

前言引入

当今市面上用于权限管理的流行的技术栈组合是

  • ssm+shrio
  • SpringCloud+SpringBoot+SpringSecurity

在这里插入图片描述

这种搭配自然有其搭配的特点,由于SpringBoot的自动注入配置原理,在创建项目时就自动注入管理SpringSecurity的过滤器容器(DelegatingFilterProxy),而这个过滤器是整个SpringSercurity的核心。掌握着SpringSercurity整个权限认证过程,而SpringBoot很香的帮你将其自动注入了,而用ssm
去整合Security,将会耗用大量的配置文件,不易于开发,而Security的微服务权限方案,更是能和Cloud完美融合,于是Security比Shrio更强大,功能更齐全。

Security的核心配置文件

核心:Class SecurityConfig extends WebSecurityConfigurerAdapter

继承了WebSecurityConfigurerAdapter后我们关注于configure方法对于在整个安全认证的过程进行相关的配置,当然在配置之前我们先简单了解一下流程

在这里插入图片描述

简单的看了整个权限认证的流程,很轻易的总结得出,SpringSecurity核心的就是以下几种配置项了

  • 拦截器(Interceptor)
  • 过滤器(Filter)
  • 处理器(Handler,异常处理器,登录成功处理器)

那我们就首先通过配置来完成认证过程吧!!!!


Security的认证过程

假设我们要实现一下的认证功能
1. 是登录请求

  • 我们需要先判断验证码是否正确(验证码过滤器,通过addFilerbefore实现前置拦截)
  • 再判断用户名密码是否正确(使用自带的用户名密码过滤器,UsernamePasswordAuthenticationFilter
  • 配置异常处理器(Handler)通过IO流将异常信息写出

关于密码校验的流程:
UsernamePasswordAuthenticationFilter的密码校验规则是基于AuthenticationManagerBuilder(认证管理器)下的 UserDetailsService里的规则进行验证的:
其中的核心方法:

1.public UserDetails *loadUserByUsername(String username)
通过请求参数的用户名去数据库查询是否存在,存在则将其封装在UserDetails里面,而验证过程是通过AuthenticationManagerBuilder获取到UserDetail里的username和password来校验的,
这样我们就可以通过

  1. 配置yaml文件设置账号密码
  2. 通过数据库结合UserDetail来设置账号密码
  3. 校验密码的过程是,前端请求过来的用户名和密码存储在验证的token中,而我们请求的同时我们要将数据库中原本的User对象获取到,并

(UserDetailsService中的方法,注意需要将UserDetailsService注入AuthenticationManagerBuilder中)

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		SysUser sysUser = sysUserService.getByUsername(username);
		if (sysUser == null) {
			throw new UsernameNotFoundException("用户名或密码不正确");
		}
		// 注意匹配参数,前者是明文后者是暗纹
		System.out.println("是否正确"+bCryptPasswordEncoder.matches("111111",sysUser.getPassword()));
		return new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), getUserAuthority(sysUser.getId()));
	}

通过了这个验证后,过滤器放行,不通过就用自定义或者默认的处理器处理


核心配置文件:

package com.markerhub.config;

import com.markerhub.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	LoginFailureHandler loginFailureHandler;

	@Autowired
	LoginSuccessHandler loginSuccessHandler;

	@Autowired
	CaptchaFilter captchaFilter;

	@Autowired
	JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	JwtAccessDeniedHandler jwtAccessDeniedHandler;

	@Autowired
	UserDetailServiceImpl userDetailService;

	@Autowired
	JwtLogoutSuccessHandler jwtLogoutSuccessHandler;

	@Bean
	JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
		JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager());
		return jwtAuthenticationFilter;
	}

	@Bean
	BCryptPasswordEncoder bCryptPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}

	private static final String[] URL_WHITELIST = {

			"/login",
			"/logout",
			"/captcha",
			"/favicon.ico",

	};


	protected void configure(HttpSecurity http) throws Exception {

		http.cors().and().csrf().disable()

				// 登录配置
				.formLogin()
				.successHandler(loginSuccessHandler)
				.failureHandler(loginFailureHandler)

				.and()
				.logout()
				.logoutSuccessHandler(jwtLogoutSuccessHandler)

				// 禁用session
				.and()
				.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS)

				// 配置拦截规则
				.and()
				.authorizeRequests()
				.antMatchers(URL_WHITELIST).permitAll()
				.anyRequest().authenticated()

				// 异常处理器
				.and()
				.exceptionHandling()
				.authenticationEntryPoint(jwtAuthenticationEntryPoint)
				.accessDeniedHandler(jwtAccessDeniedHandler)

				// 配置自定义的过滤器
				.and()
				.addFilter(jwtAuthenticationFilter())
				.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)

		;

	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailService);
	}
}

2. 不是登录请求

  • 通过JwtfFilter来查看是否为登录状态

使用Redis整合时的注意事项

本质上还是编写过滤器链:

  • 在登录请求前添加过滤器
  • 注意验证码存储在redis的失效时间,如果超过失效时间将会被验证码拦截器拦截下来
  • 需要准备一个生成验证码的接口,存储在Redis中
  • 使用完验证码需要将其删除
// 校验验证码逻辑
	private void validate(HttpServletRequest httpServletRequest) {

		String code = httpServletRequest.getParameter("code");
		String key = httpServletRequest.getParameter("token");

		if (StringUtils.isBlank(code) || StringUtils.isBlank(key)) {
			System.out.println("验证码校验失败2");
			throw new CaptchaException("验证码错误");
		}

		System.out.println("验证码:"+redisUtil.hget(Const.CAPTCHA_KEY, key));
		if (!code.equals(redisUtil.hget(Const.CAPTCHA_KEY, key))) {
			System.out.println("验证码校验失败3");
			throw new CaptchaException("验证码错误");
		}

		// 一次性使用
		redisUtil.hdel(Const.CAPTCHA_KEY, key);
	}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值