SpringSecurity的基本原理

SpringSecurity的基本原理

SpringSecurity框架作为一款安全框架,可以拦截我们的请求,默认情况下使用的是basic认证的方式,如图一那样。但是,这种方式很不友好,每次的密码都是动态的变化的,无法自定义,我们可以在application.properties配置security.basic.enabled=false进行关闭。但是此时谁都可以访问我们的接口,很不安全。此时可以在在跟包下创建一个BrowswerSecurityConfig类,集成WebSecurityConfiguerAdapter适配器,此时访问就是如图二那样 http.formLogin()//表示使用表单登录 .and() .authorizeRequests()//表示对请求的授权 .anyRequest()//对任何请求授权 .authenticated();//都需要身份认证

SpringSecurity的原来剖析

SpringSecurity实际上是一系列的过滤器链

1.UsernamepasswordAuthenticationFilter主要负责表单的认证

2.BasicAuthenticationFilter主要负责SpringSecurity默认的basic认证

3.ExceptionTranslationFilter:根据抛出的错误跳转到对应的页面

4.FilterSecurityInterceptor是过滤器类,根据我们的配置是否可以访问后台的接口

自定义用户认证逻辑

1.处理用户信息获取逻辑

要获取用户的信息就需要实现UserDetailsService,并且实现UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;方法

2.处理用户校验逻辑 用户的校验可以使用关联我们的数据库,根据数据库的数据来进行校验,校验之后会返回一个UserDtails对象。这个对象类如下:

public interface UserDetails extends Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();
	//用户名
	String getPassword();
	//密码
	String getUsername();
	//账户是否超时
	boolean isAccountNonExpired();
	//账户是否被冻结
	boolean isAccountNonLocked();
	//密码是否失效
	boolean isCredentialsNonExpired();
	//是否可用
	boolean isEnabled();
}

3.处理密码加密解密 需要在BrowswerSecurityConfig.java配置passwordEncoder加密器

	/**
	 * 配置密码编码器
	 * [@return](https://my.oschina.net/u/556800)
	 */
	[@Bean](https://my.oschina.net/bean)
	public PasswordEncoder passwordEncoder(){
		return new BCryptPasswordEncoder();
	}

在MyUserDetailsService中的代码如下:

[@Component](https://my.oschina.net/u/3907912)
public class MyUserDetailService implements UserDetailsService {

	@Autowired
	PasswordEncoder passwordEncoder;
   private Logger logger = LoggerFactory.getLogger(MyUserDetailService.class);
	[@Override](https://my.oschina.net/u/1162528)
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
	   logger.info("登录用户名:"+s);

		String password = passwordEncoder.encode("123");
		logger.info("用户密码:"+password);
		return new User(s,password/*"123"*/,
				true,true,true,true,
				AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
	}
}

个性化用户认证流程

1.自定义登录页面 自定义登录页面需要在代码中配置如下的代码:

			  http
				.formLogin()//表示使用表单登录
				.loginPage("/imooc-signIn.html")//表示条状登录页面
				.and()
				.authorizeRequests()//表示对请求的授权
				.anyRequest()//对任何请求授权
				.authenticated();//都需要身份认证

但是如果这样以访问会出问题,因为即使是访问登录页面,也需要登录认证,会出现多次访问的情况:

此时代码还需要放权,添加的代码如下:

		.antMatchers("/imooc-signIn.html")
		.permitAll()

加了这个放行的代码之后再次访问会出现如下的显示:

默认情况下的UsernamepasswordAthenticationFilter过滤的路径是/login,源码:

	public UsernamePasswordAuthenticationFilter() {
		super(new AntPathRequestMatcher("/login", "POST"));
	}

为了让SpringSecurity能够过滤我们自定义的路径,如我们表单的路径/authentication/form,需要在BrowswerSecurityConfig.java内配置如下:

				.loginProcessingUrl("/authentication/form")//表示让过滤器处理我们自定义的路径的

此时在此登录会发现如下情况(因为SpringSecurity提供了跨站访问伪造的一个防护):

此时可以想关闭这个防护

	 .and()
	 .csrf().disable();//关闭跨站伪造的防护

处理不同类型的请求

需要添加一个controller,代码如下所示:

@RestController
public class BrowserSecurityController {
	private Logger logger = LoggerFactory.getLogger(BrowserSecurityController.class);
	//请求缓存
	private RequestCache requestCache = new HttpSessionRequestCache();
	//调转工具
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
	@Autowired
	SecurityProperties securityProperties;
	/**
	 * 当需要省份认证时跳转到这里
	 *
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("/authentcation/require")
	@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
	public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
		//拿到引发跳转的请求
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest != null) {
			String targetUrl = savedRequest.getRedirectUrl();
			logger.info("引发跳转的请求是:" + targetUrl);
			//如果请求的是html,那么就直接到登录的html
			if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
				redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
			}
		}
		return new SimpleResponse("访问的服务需要省份认证,请引导用户到登录页面");
	}
}

除了添加上年的controller代码之外还需要在配置添加如下的代码:

			   http
//            .httpBasic()//使用的是SpringSecurity的基本认证
				.formLogin()//表示使用表单登录
				.loginPage("/authentcation/require")//通过controller去处理不同类型的请求
				.loginProcessingUrl("/authentication/form")//表示让过滤器处理我们自定义的路径的
				.and()
				.authorizeRequests()//表示对请求的授权
				//表示对登录的页面允许访问,不需要授权
				.antMatchers("/authentcation/require",securityProperties.getBrowser().getLoginPage())
				.permitAll()
				.anyRequest()//对任何请求授权
				.authenticated()//都需要身份认证
				.and()
				.csrf().disable();//关闭跨站伪造的防护

2.自定义登录成功处理 需要实现AuthenticationSuccessHandler接口

@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
	private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationSuccessHandler.class);
	@Autowired
	private ObjectMapper objectMapper;
	/**
	 *
	 * @param request
	 * @param response
	 * @param authentication 包装了所有的关于用户的信息
	 * @throws IOException
	 * @throws ServletException
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
	logger.info("登录成功");
	response.setContentType("application/json;charset=UTF-8");
	//将authentication以json字符串显示回去
	response.getWriter().write(objectMapper.writeValueAsString(authentication));
	}
}

此时已经完成了自定义的的登录成功的处理器的编写,但是仍然无法使用,需要注册进去

/**
 * 专门用来做web安全应用的适配器WebSecurityConfigurerAdapter
 */
@Configuration
public class BrowswerSecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	SecurityProperties securityProperties;
	/**
	 * 登录成功处理器
	 */
	@Autowired
	ImoocAuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
	/**
	 * 登录失败处理器
	 */
	@Autowired
	ImoocAuthenticationFailureHandler imoocAuthenticationFailureHandler;

	/**
	 * 配置密码编码器
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder(){
		return new BCryptPasswordEncoder();
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
			   http
//            .httpBasic()//使用的是SpringSecurity的基本认证
				.formLogin()//表示使用表单登录
				.loginPage("/authentcation/require")//表示条状登录页面
				.loginProcessingUrl("/authentication/form")//表示让过滤器处理我们自定义的路径的
				.successHandler(imoocAuthenticationSuccessHandler)//注册我们的登录成功的处理器
				.failureHandler(imoocAuthenticationFailureHandler)//注册我们自定义的登录失败处理器
				.and()
				.authorizeRequests()//表示对请求的授权
				//表示对登录的页面允许访问,不需要授权
				.antMatchers("/authentcation/require",securityProperties.getBrowser().getLoginPage())
				.permitAll()
				.anyRequest()//对任何请求授权
				.authenticated()//都需要身份认证
				.and()
				.csrf().disable();//关闭跨站伪造的防护

	}
}

此时登录访问,登录成功会出现如下的表现:

3.自定义登录失败处理

需要实现AuthenticationFailureHandler接口

@Component("imoocAuthenticationFailureHandler")
public class ImoocAuthenticationFailureHandler implements AuthenticationFailureHandler {
	private Logger logger = LoggerFactory.getLogger(ImoocAuthenticationFailureHandler.class);
	@Autowired
	private ObjectMapper objectMapper;
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
		logger.info("登录失败");
		response.setContentType("application/json;charset=UTF-8");
		response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());//设置服务器错误
		//将authentication以json字符串显示回去
		response.getWriter().write(objectMapper.writeValueAsString(exception));

	}
}

完成了处理器的代码编写统一需要注册,登录访问失败会有如下的返回值:

上面的自定义处理器存在的问题

前端是表单提交的时候可能就需要直接跳转到登录之前的路径,此时就需要使用跳转,否则就返回json合适

为了实现可以给用户配置,在配置类添加一个登录的类型

	public enum LoginType {
		REDIRECT,//跳转
		JSON//返回json
	}

	public class BrowserProperties {
		private String loginPage = "/imooc-signIn.html";
		private LoginType loginType = LoginType.JSON;

		public BrowserProperties() {
		}

		public String getLoginPage() {
			return loginPage;
		}

		public void setLoginPage(String loginPage) {
			this.loginPage = loginPage;
		}

		public LoginType getLoginType() {
			return loginType;
		}

		public void setLoginType(LoginType loginType) {
			this.loginType = loginType;
		}
	}

在登录成功失败添加登录类型判断:

   if (LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())) {
			response.setContentType("application/json;charset=UTF-8");
			response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());//设置服务器错误
			//将authentication以json字符串显示回去
			response.getWriter().write(objectMapper.writeValueAsString(exception));
		}else {
		}

在代码添加好这些功能之后就可以在配置文件配置:

imooc.security.browser.loginType=REDIRECT

转载于:https://my.oschina.net/u/3474937/blog/3024518

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值