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
相關文章
相關標籤/搜索