工作流程:
1.過濾器filter 檢查 (filter要設置manager)
2.provider 來 校驗 授權 (manage加載provider)
3.EntryPoint來處理 授權可能會出現的異常
1.pom.xml中導入依賴
<denpendency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security<artifactId>
</denpendency>
2.配置類 SecurityConfig
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private Parameters parameters; @Autowired private CommonCacheUtils cacheUtils; //設置 filter的get方法, 爲其配置 AuthenticationManager private RestPreAuthenticatedProccessingFilter getRestPreAuuthenticatedProcessingFilter() throws Exception { RestPreAuthenticatedProccessingFilter filter=new RestPreAuthenticatedProccessingFilter(parameters.getSpringSecurityPermitUrl(),cacheUtils); filter.setAuthenticationManager(this.authenticationManagerBean()); return filter; } @Override //AuthenticationManager 構造器 protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(new RestAuthenticationProvider()); //爲manager添加一個Provider } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(parameters.getSpringSecurityPermitUrl().toArray(new String[parameters.getSpringSecurityPermitUrl().size()])).permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不需要session,無狀態的請求 .and().httpBasic().authenticationEntryPoint(new RestAuthenticationEntryPoint()) .and().addFilter(getRestPreAuuthenticatedProcessingFilter()) ; } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers(HttpMethod.OPTIONS,"/**"); //忽略 攔截option方法的請求 } }
3.自定義filter 檢查 請求的信息
package com.sean.mamabike.security; import com.sean.mamabike.cache.CommonCacheUtils; import com.sean.mamabike.common.constants.Constants; import com.sean.mamabike.user.entity.UserElement; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.util.AntPathMatcher; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; @Slf4j //spring security的 預 過濾器 public class RestPreAuthenticatedProccessingFilter extends AbstractPreAuthenticatedProcessingFilter { private AntPathMatcher matcher=new AntPathMatcher(); //spring 提供的 路徑 匹配器 private List<String> springSecurityPermitUrl; //需要放過的請求列表 構造器用 private CommonCacheUtils cacheUtils; //redis 工具 public RestPreAuthenticatedProccessingFilter(List<String> springSecurityPermitUrl, CommonCacheUtils cacheUtils) { this.springSecurityPermitUrl = springSecurityPermitUrl; this.cacheUtils = cacheUtils; } @Override // 獲取 用戶信息 protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { List<GrantedAuthority> authorityList=new ArrayList<>(); //授權列表 //1.不需要 權限 檢查 的請求(login,register..... if (isPermitUrl(request.getRequestURI().toString())||"OPTIONS".equals(request.getMethod())){ GrantedAuthority grantedAuthority=new SimpleGrantedAuthority("USER_OTHERS"); //授權了一個 其他 權限 authorityList.add(grantedAuthority); //授權列表 加載 角色 return new RestAuthenticationToken(authorityList); //用戶信息 } // 2. 其他 正常的 請求 String version=request.getHeader(Constants.REQUEST_VERSION); //檢查app版本 String token=request.getHeader(Constants.REQUEST_TOKEN); if (version == null){ //若版本信息爲空 request.setAttribute("header_error_code",400); //設置錯誤信息 } if (request.getHeader("header_error_code") == null){ //如果沒有錯誤信息,就檢查token try{ if (!StringUtils.isBlank(token)) { //token不爲空 UserElement ue = cacheUtils.getUserElementByToken(token); //獲取redis裏的ue if (ue instanceof UserElement) { GrantedAuthority authority = new SimpleGrantedAuthority("USER_BIKE"); //授權一個 單車 權限 authorityList.add(authority); RestAuthenticationToken authToken = new RestAuthenticationToken(authorityList); authToken.setUserElement(ue); return authToken; } else { log.warn("token is empty"); request.setAttribute("header_error_code", 401); } } }catch (Exception e){ log.error("fail to authenticate user ",e); } } //3. 請求頭有錯誤信息 if (request.getHeader("header_error_code") != null){ GrantedAuthority authority=new SimpleGrantedAuthority("USER_Error"); //設置一個 錯誤 權限 authorityList.add(authority); } return new RestAuthenticationToken(authorityList); } //匹配請求 路徑,判斷是否放過 private boolean isPermitUrl(String uri) { boolean result=false; if (springSecurityPermitUrl != null){ for (String s:springSecurityPermitUrl){ if(matcher.match(s,uri)){ result=true; break; } } } return result; } @Override //獲取 密碼 protected Object getPreAuthenticatedCredentials(HttpServletRequest httpServletRequest) { return null; } }
4.自定義provider 判斷是否通過security驗證
package com.sean.mamabike.security; import com.sean.mamabike.common.exception.BadCredentialException; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; //spring security 的provider 來 校驗 身份信息 public class RestAuthenticationProvider implements AuthenticationProvider { /** *@Author sean *@Date 2017/10/14 16:17 *@Description 第二步:校驗信息,授權 */ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication instanceof PreAuthenticatedAuthenticationToken){ PreAuthenticatedAuthenticationToken preAuth= (PreAuthenticatedAuthenticationToken) authentication; //獲取 傳輸的token RestAuthenticationToken restAuth= (RestAuthenticationToken) preAuth.getPrincipal(); //獲取自定義的token if (restAuth.getAuthorities() != null&& restAuth.getAuthorities().size()>0) { //獲取權限列表,並且不爲null,大於0 GrantedAuthority authority=restAuth.getAuthorities().iterator().next(); if ("USER_BIKE".equals(authority.getAuthority())){ return restAuth; }else if ("USER_OTHERS".equals(authority.getAuthority())){ return restAuth; } } }else if (authentication instanceof RestAuthenticationToken){ RestAuthenticationToken restAuth= (RestAuthenticationToken) authentication; //獲取自定義的驗證token if (restAuth.getAuthorities() !=null&&restAuth.getAuthorities().size()>0){ GrantedAuthority authority=restAuth.getAuthorities().iterator().next(); if ("USER_BIKE".equals(authority.getAuthority())){ return restAuth; }else if ("USER_OTHERS".equals(authority.getAuthority())){ return restAuth; } } } throw new BadCredentialException("unknown"); } /** *@Author sean *@Date 2017/10/13 16:06 *@Description 1.判斷傳過來token的類型,進行判斷 * 如果return true,則執行上面的authenticate 驗證 * 如果return false, 報異常到entryPoint */ @Override public boolean supports(Class<?> authentication) { //判斷傳過來的token是否是 定義的token return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication)||RestAuthenticationToken.class.isAssignableFrom(authentication); } }
5.配置 EntryPoint 異常處理
package com.sean.mamabike.security; import com.alibaba.fastjson.JSON; import com.sean.mamabike.common.resp.ApiResult; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.*; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j //統一異常處理 public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { ApiResult resp=new ApiResult(); //檢查頭部裏面帶的錯誤信息 if ("400".equals(request.getHeader("header_error_code")+"")){ resp.setCode(400); resp.setMessage("請升級至最新客戶端"); }else if ("401".equals(request.getHeader("header_error_code"))){ resp.setCode(401); resp.setMessage("請登陸後再嘗試"); } // 解決 跨域請求的問題 把結果以json的形式返回給 前端 (前後端分離的時候都要跨域) try { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with"); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); response.getWriter().write(JSON.toJSONString(resp)); //把信息返回給前端 response.flushBuffer(); } catch (IOException e1) { log.error("fail to send error info to user",e.getMessage()); } } }