SpringSecurity的基本原理

SpringSecurity的基本原理

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

SpringSecurity的原来剖析

SpringSecurity其实是一系列的过滤器链前端

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

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

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

4.FilterSecurityInterceptor是过滤器类,根据咱们的配置是否能够访问后台的接口json

自定义用户认证逻辑

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加密器app

/**
	 * 配置密码编码器
	 * [@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
相关文章
相关标签/搜索