SpringSecurity框架做爲一款安全框架,能夠攔截咱們的請求,默認狀況下使用的是basic認證的方式,如圖一那樣。可是,這種方式很不友好,每次的密碼都是動態的變化的,沒法自定義,咱們能夠在application.properties配置security.basic.enabled=false進行關閉。可是此時誰均可以訪問咱們的接口,很不安全。此時能夠在在跟包下建立一個BrowswerSecurityConfig類,集成WebSecurityConfiguerAdapter適配器,此時訪問就是如圖二那樣 http.formLogin()//表示使用表單登陸 .and() .authorizeRequests()//表示對請求的受權 .anyRequest()//對任何請求受權 .authenticated();//都須要身份認證html
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