springSecurity是spring官方給咱們提供的一個很是強大的一個安全框架。也是如今最受歡迎的安全框架,比shiro更強大html
springSecurity主要工做原理是內置了許多過濾器,組成過濾器鏈,每一個過濾器都有本身的明確分工,而後還有異常處理類,還有最後的一個認證受權類。看圖java
綠色的是表明過濾器鏈,咱們能夠本身配置增長和刪除,藍色的是異常處理類,後面黃色的是最後的認證處理類,這兩個類的位置是不會變的,因此說,最終咱們的請求會到filterSecurityInterceptor這個類中來判斷到底能不能訪問咱們請求的url web
好比說:咱們直接一個請求(不是登錄請求),他先會到filterSecurityInterceptor這個類,而後驗證,沒有成功就會拋出異常,讓ExceptionTranslationFiter這個類來處理(實際上就是一個轉發到登陸頁面),而後填上帳號密碼,登錄,會讓usernamepasswordAuthenticationFilter這個類攔截,而後最後仍是在filterSecurityInterceptor這個類來判斷是否要對咱們所請求的url作請求算法
看下面的一個簡單demospring
BrowserSecurityConfig.java數據庫
package com.imooc.security.browser; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * Created by 敲代碼的卡卡羅特 * on 2018/4/15 21:48. */ @Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // http.httpBasic() //彈出框請求 http.formLogin() //表單驗證 .and() .authorizeRequests() //開啓認證請求 .anyRequest() //任何請求 .authenticated(); //都須要認證 } }
聲明一個配置文件,配置最基本的功能。apache
而後咱們再訪問url就須要登錄了,原始的用戶名是user,密碼是框架系統生成了驗證碼,咱們須要改變一下。用咱們自定義的帳號和密碼。json
MyUserDetailService.java (最終返回一個User對象) 自定義用戶很簡單就是@Component 這個類,讓spring管理就行
package com.imooc.security.browser; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; /** * Created by 敲代碼的卡卡羅特 * on 2018/4/15 22:08. */ @Component public class MyUserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //、、、、、、省略根據用戶名從數據庫獲得user對象的過程 這個方法就是根據登錄的用戶名而後返回一個user對象,這個user對象是框架提供給咱們的 //繼承了UserDetails這個類。咱們也能夠自定義類繼承UserDetails return new User(s,"123", AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }
UserDetails.java 源碼解析緩存
public interface UserDetails extends Serializable { //權限集合 Collection<? extends GrantedAuthority> getAuthorities(); //密碼 String getPassword(); //帳號 String getUsername(); //帳號是否過時 boolean isAccountNonExpired(); //帳號是否鎖定 boolean isAccountNonLocked(); //密碼是否過時 boolean isCredentialsNonExpired(); //帳號是否可用 boolean isEnabled(); }
密碼--》很簡單,只須要在配置文件配置一下這個bean就行, BCryptPasswordEncoder這個類是框架給咱們提供的。安全
可是咱們在日常用的時候確定密碼存的明文,因此咱們須要一個加密算法,咱們來配一個加密類。
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
若是咱們還想更加定製化一些呢?
好比說:登錄成功去那個方法,登錄失敗去那一個方法。爲了方便記錄日誌嘛。固然。這些能夠在配置文件中配置,很是簡單。看代碼
自定義成功和失敗的處理類,只須要new一個類實現框架給的特定的接口就行。而後在配置文件中配置
ImoocAuthenticationSuccessHandler.java //成功處理類
/** * */ package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.security.core.properties.LoginResponseType; import com.imooc.security.core.properties.SecurityProperties; /** * @author zhailiang * */ @Component("imoocAuthenticationSuccessHandler") public class ImoocAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; //springboot默認注入了這個類,處理json的 @Autowired private SecurityProperties securityProperties; //自定義的參數類,不用管。就是配置的url之類的。 /* * (non-Javadoc) * * @see org.springframework.security.web.authentication. * AuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http. * HttpServletRequest, javax.servlet.http.HttpServletResponse, * org.springframework.security.core.Authentication) */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { // 注意authentication這個參數可牛了 這裏面封裝了你當前對象的全部信息 logger.info("登陸成功"); //這是業務邏輯,不用管,就是說若是設置了json格式,就返回前臺json,若是不是就返回頁面 if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(authentication)); } else { //調用父類的方法,其實就是跳轉到原來的頁面 super.onAuthenticationSuccess(request, response, authentication); } } }
onAuthenticationFailure.java //失敗處理類
/** * */ package com.imooc.security.browser.authentication; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; import com.imooc.security.browser.support.SimpleResponse; import com.imooc.security.core.properties.LoginResponseType; import com.imooc.security.core.properties.SecurityProperties; /** * @author zhailiang * */ @Component("imoocAuthenctiationFailureHandler") public class ImoocAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private ObjectMapper objectMapper; @Autowired private SecurityProperties securityProperties; /* (non-Javadoc) * @see org.springframework.security.web.authentication.AuthenticationFailureHandler#onAuthenticationFailure(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.AuthenticationException) */ @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { logger.info("登陸失敗"); if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage()))); }else{ super.onAuthenticationFailure(request, response, exception); } } }
BrowserSecurityConfig.java //配置文件
package com.imooc.security.browser; import com.imooc.security.browser.authentication.ImoocAuthenctiationFailureHandler; import com.imooc.security.browser.authentication.ImoocAuthenticationSuccessHandler; 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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * Created by 敲代碼的卡卡羅特 * on 2018/4/15 21:48. */ @Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired ImoocAuthenticationSuccessHandler imoocAuthenticationSuccessHandler; @Autowired ImoocAuthenctiationFailureHandler imoocAuthenctiationFailureHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { // http.httpBasic() //彈出框請求 http.formLogin() //表單驗證 .loginPage("/authentication/require") //返回登陸頁,能夠是一個action,也能夠是一個html頁面 .loginProcessingUrl("/authentication/form") //點擊登錄的action,默認是login 咱們能夠自定義 .successHandler(imoocAuthenticationSuccessHandler) //配置登錄成功的處理類 .failureHandler(imoocAuthenctiationFailureHandler)//配置登錄失敗的處理類 .and() .authorizeRequests() //開啓認證請求 .antMatchers("/imooc-signIn.html","/authentication/require").permitAll()//忽略認證 .anyRequest() //任何請求 .authenticated(); //都須要認證 http.csrf().disable(); //關閉csrf } }
自定義未登錄跳轉接口 (也就是進一步細化判斷,寫日誌什麼的,日常咱們在配置文件中配的就是跳轉到登錄頁面,這是跳轉到接口)
package com.imooc.security.browser; import com.imooc.security.browser.support.SimpleResponse; import com.imooc.security.core.properties.SecurityConstants; import com.imooc.security.core.properties.SecurityProperties; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.savedrequest.SavedRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created by 敲代碼的卡卡羅特 * on 2018/4/16 0:16. */ @RestController public class BrowserSecurityController { private Logger logger = LoggerFactory.getLogger(getClass()); private RequestCache requestCache = new HttpSessionRequestCache(); //提供用緩存中獲得url的類 private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); //提供跳轉的類 @Autowired private SecurityProperties securityProperties; /** * 當須要身份認證時,跳轉到這裏 * * @param request * @param response * @return * @throws IOException */ @RequestMapping(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) @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); if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) { redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage()); } } return new SimpleResponse("訪問的服務須要身份認證,請引導用戶到登陸頁"); } }
最後:咱們能夠在代碼的任何地方拿到當前登錄對象的信息(首先是已經登陸) 有三種方法
@GetMapping("/user1") public Object getUser1() { return SecurityContextHolder.getContext().getAuthentication(); } @GetMapping("/user2") public Object getUser2(Authentication authentication) { return authentication; } @GetMapping("/user3") public Object getUser3(@AuthenticationPrincipal UserDetails user) { return user; }
實現記住我功能 ,配置很簡單,業務邏輯是,你配置好框架會給你在你數據庫中建立一個表,包含用戶名,token,時間等字段,而後也會在你本地存個cookie,登錄完成在表中插入一條數據,若是下次登錄的話,驗證你信息,若是沒有攜帶認證信息,最後會查你的數據庫,而後獲得你用戶名,而後再調那個根據用戶名獲得用戶的方法認證成功。看代碼
@Autowired private UserDetailsService userDetailsService; @Autowired private SecurityProperties securityProperties; @Bean public PersistentTokenRepository persistentTokenRepository() { JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); //new一個這個類 tokenRepository.setDataSource(dataSource); //指定數據源,由於要存數據庫 tokenRepository.setCreateTableOnStartup(true);//在啓動的時候建立表 return tokenRepository; } @Override protected void configure(HttpSecurity http) throws Exception { // http.httpBasic() //彈出框請求 http.formLogin() //表單驗證 .loginPage("/authentication/require") //返回登陸頁,能夠是一個action,也能夠是一個html頁面 .loginProcessingUrl("/authentication/form") //點擊登錄的action,默認是login 咱們能夠自定義 .successHandler(imoocAuthenticationSuccessHandler) //配置登錄成功的處理類 .failureHandler(imoocAuthenctiationFailureHandler)//配置登錄失敗的處理類 .and() .rememberMe() //記住我 .tokenRepository(persistentTokenRepository()) //指定TokenRepository的bean .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())//過時時間 .userDetailsService(userDetailsService)//指定根據名字獲得用戶的類 .and() .authorizeRequests() //開啓認證請求 .antMatchers("/imooc-signIn.html","/authentication/require").permitAll()//忽略認證 .anyRequest() //任何請求 .authenticated(); //都須要認證 http.csrf().disable(); //關閉csrf }
最後:前臺傳過來的必定是 name="remember-me" 這個字段