SpringSecurity學習於實踐

SpringSecurity的核心功能

1.認證(你是誰) 2.受權(能幹嗎) 3.攻擊防禦(防止僞造身份)html

SpringSecurity的原理

其實Spring Security是一系列的過濾器鏈 java

在這條顧慮其鏈上,若是咱們定義的是表單認證,那麼UsernamePasswordAuthenticationFilter就會起做用,若是是Basic認證,那麼BasicAuthenticationFilter就起做用,這條鏈上若是不相關的是不會起做用的。最後到達FilterSecurityInterceptor來判斷是否能夠訪問REST API,若是不符合就拋出異常,此時ExceptionTranslationFilter起做用。spring

自定義用戶認證

處理用戶信息獲取邏輯

用戶的信息被封裝在了org.springframework.security.core.userdetails.UserDetailsService, 若是須要從本身的數據源獲取用戶數據,那麼就須要實現UserDetailsService類,並實現loadUserByUsername(String s)json

@Component//TODO 使之成爲用戶的bean
public class MyDetailService implements UserDetailsService {
	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	[@Override](https://my.oschina.net/u/1162528)
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根據用戶名查找用戶信息
		logger.info("登錄用戶名:"+s);
		return new User(s,"123456", AuthorityUtils.createAuthorityList("admin"));
}
}

處理用戶校驗邏輯

能夠不使用SpringSecurity的User,能夠自定義一個類實現UserDetail,實現它的下面的四個方法。 //帳戶是否過時 boolean isAccountNonExpired(); //帳戶是否被鎖 boolean isAccountNonLocked(); //證書是否過時 boolean isCredentialsNonExpired(); //是否可用 boolean isEnabled();緩存

例子:session

@Component//TODO 使之成爲用戶的bean
public class MyDetailService implements UserDetailsService {
	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	[@Override](https://my.oschina.net/u/1162528)
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根據用戶名查找用戶信息
		logger.info("登錄用戶名:"+s);
		return new User(s,"123456",true,true,true,false, AuthorityUtils.createAuthorityList("admin"));
	}
}

處理密碼加密解密

須要配置一個加密器:app

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
	//生成一個加密器
	@Bean
	public PasswordEncoder getPasswordEncoder(){
		return new BCryptPasswordEncoder();
	}
....}

@Component//TODO 使之成爲用戶的bean
public class MyDetailService implements UserDetailsService {
**		@Autowired
	PasswordEncoder passwordEncoder;**

	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	@Override
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根據用戶名查找用戶信息
		logger.info("登錄用戶名:"+s);
		String encode = passwordEncoder.encode("123456");
		logger.info("登錄用戶名:"+encode);
		return new User(s,encode,true,true,true,true, AuthorityUtils.createAuthorityList("admin"));
	}
}

執行結果:ide

2018-11-28 21:23:10.046  INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService     : 登錄用戶名:user
2018-11-28 21:23:10.133  INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService     : 登錄用戶名:$2a$10$S7UuQCqoIEnfzJJGZOnJuuavzZFlohoXA4IpGDkmxmmV9H.01XUIS

自定義認證流程

自定義的登陸界面

http.formLogin().loginPage("/imooc-signIn.html")//這裏能夠寫登陸的界面的url,可是在以前的配置已經默認的攔截因此的url,因此會出現循環的打開登陸的界面,致使死循環,爲了解決這種狀況就須要使用.antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陸的時候受權。此時在點擊任何該服務的連接就會跳到登陸界面,例如:測試

直接引導登陸界面

http.formLogin()
            .loginPage("/imooc-signIn.html")
            .and()
            .authorizeRequests()//對後面的請求受權
            .antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陸的時候受權。
            .anyRequest()//任何請求
            .authenticated();//都須要身份認證

實現自定義登陸界面

1.須要請求跳轉。flex

2.存儲跳轉前的請求到httpSessionRequest裏面。

3.將請求從緩存取出。

4.判斷請求跳轉時頁面跳轉仍是其餘的,若是頁面就能夠時直接跳轉(可是要自定義跳轉的登陸頁面就須要須要實現登陸頁面時能夠定製化的)。

5.鑑定權限,若是不是就登陸頁面的請求跳轉就是權限不夠提示401。

例子: SecurityProperties.java

@ConfigurationProperties(prefix = "flexible.security")
public class SecurityProperties {
	private BrowserProperties browser = new BrowserProperties();

	public BrowserProperties getBrowser() {
		return browser;
	}

	public void setBrowser(BrowserProperties browser) {
		this.browser = browser;
	}
}

BrowserProperties.java

public class BrowserProperties {
	//配置默認的登陸頁
	private String loginPage="/imooc-signIn.html";

	public BrowserProperties() {
	}

	public String getLoginPage() {
		return loginPage;
	}

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

SecurityCoreConfig.java(讓配置生)

/**
 * 讓咱們配置的security配飾生效
 */
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}

BrowserSecurityConfig.java(配置放行路徑跳轉到本身的路徑)

http.formLogin()
				.loginPage("/authentication/require")//自定義的認證路徑
				.loginProcessingUrl("/authentication/form")//告訴springsecurity使用UsernamePasswordAuthenticationFilter來處理登陸
				.and()
				.authorizeRequests()//對後面的請求受權,將自定義的登陸頁面頁放權
				.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll()//表示匹配到登陸的時候受權。
				.anyRequest()//任何請求
				.authenticated()//都須要身份認證
				.and().csrf().disable();//將spring security爲了防止CSRF攻擊禁用

BrowserSecurityController.java

@RestController
public class BrowserSecurityController {
	Logger logger = LoggerFactory.getLogger(BrowserSecurityController.class);
	@Autowired
	SecurityProperties securityProperties;//獲取到自定義的登陸頁
	//拿到引起跳轉的請求須要經過HttpSessionRequestCache來拿
	private RequestCache requestCache = new HttpSessionRequestCache();//在跳轉以前,springsecurity將請求緩存在這個httpSessionRequestCache裏面
	//實現的跳轉的一個類
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	/**
	 * 須要身份認證會跳轉到這裏
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("authentication/require")
	@ResponseStatus(code = HttpStatus.UNAUTHORIZED)//不是一個html就返回一個401的狀態碼
	public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
		//拿到在跳轉以前緩存的請求
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest != null) {
			String target = savedRequest.getRedirectUrl();
			logger.info("引起跳轉的請求是:{}", target);
			if (StringUtils.endsWithIgnoreCase(target, ".html")) {
				//跳轉,須要使用到RedirectStrategy
				redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());//這裏須要實現一個統一,通用的跳轉,不單單跳到本身的界面。
			}
		}
		return new SimpleResponse("須要用戶認證,引導到登陸頁");
	}
}

demo測試例子,在application.properties裏面配置一個flexible.security.browser.loginPage="xxx.html"就能夠了。

直接訪問請他的路徑會出現以下的狀況:

輸入demo-sigIn.html時就會跳到自定義的登陸頁

註釋掉配置的flexible.security.browser.loginPage="xxx.html"就會跳轉默認的登陸頁。

自定義登陸成功處理

須要實現AuthenticationSuccessHandler接口

FlexibleAuthenticationHandler.java

@Component(value = "flexibleAuthenticationHandler")
public class FlexibleAuthenticationHandler implements AuthenticationSuccessHandler {
	Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationHandler.class);
	@Autowired
	ObjectMapper objectMapper;
	/**
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param authentication 封裝了認證請求的信息(ip,session,用戶信息)
	 * @throws IOException
	 * @throws ServletException
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
	logger.info("....login success...");
	httpServletResponse.setContentType("application/json;charset=UTF-8");
	//將authentication對象一json格式返回去
	httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
	}
}

須要告訴SpringSecurity成功後的處理

.successHandler(flexibleAuthenticationHandler)//告訴SpringSecurity成功以後使用的處理器

自定義失敗處理

須要實現AuthenticationFailureHandler接口

FlexibleAuthenticationFailureHandler.java

@Component(value = "flexibleAuthenticationFailureHandler")
public class FlexibleAuthenticationFailureHandler implements AuthenticationFailureHandler {
	Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationFailureHandler.class);

	@Autowired
	ObjectMapper objectMapper;

	@Override
	public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authentication) throws IOException, ServletException {

		logger.info("....login failure...");
		httpServletResponse.setContentType("application/json;charset=UTF-8");
		httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
		//將authentication對象一json格式返回去
		httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
	}
}

告訴SpringSecurity失敗後的處理器:

.failureHandler(flexibleAuthenticationFailureHandler)//告訴SpringSecurity失敗後的處理器

記住個人功能的實現

實現的原理剖析

相關文章
相關標籤/搜索