SpringSecurity 基础应用

工作流程: 

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());
        }


    }
}