工作流程:
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()); } } }