前后端分离的项目,采用ssm后端接口权限校验

版权声明:不短不长八字刚好@wzy https://blog.csdn.net/qq_38089964/article/details/84584172

前后端分离的项目中,服务端对于每个请求都是一模一样的,对于每个用户的请求识别就需要用到一个统一的方式(jwt),然后在访问每个接口的时候先对这个jwt进行识别,查出这个用户的权限级别,检查是否拥有访问这个接口的权限,如果没有,那么将直接返回一个错误信息,表示权限不足,反之放过,继续往下执行。

jwt就是一个包含用户信息的加密字符串:参考:JWT的生成及验证过程

因为对于每个接口都需要进行权限校验,那么采用spring的aop最好不过了。设置每个controller都需要经过的aop切面,在切面中获取出controller中的HttpServletRequest,这个request知道了就可以获取出访问的所有headers之类的参数了。接下来就进行权限验证。

面向对象的特点之一就是继承,所以在项目中controller都需要继承一个BaseController,这样的好处之一是在controller层中可以复用一些BaseController的方法,比如获取远端ip地址、获取用户权限等常用函数。好处之二就是在权限校验切面中调用BaseController controller = (BaseController) joinPoint.getTarget();这个方法的时候可以统一转成一个对象。

BaseController一般是这样的:

public class BaseController {
  private static final Logger log = Logger.getLogger(BaseController.class);
  protected static final ThreadLocal<HttpServletRequest> requests = new ThreadLocal();
  protected static final ThreadLocal<HttpServletResponse> responses = new ThreadLocal();

  public BaseController() {
  }

  @ModelAttribute
  public void init(HttpServletRequest request, HttpServletResponse response) {
    requests.set(request);
    responses.set(response);
  }

  public HttpServletRequest getRequest() {
    return (HttpServletRequest)requests.get();
  }

  public HttpServletResponse getResponse() {
    return (HttpServletResponse)responses.get();
  }

  public void setSessionValue(String key, Object value) {
    this.getRequest().getSession().setAttribute(key, value);
  }

  public Object getSessionValue(String key) {
    return this.getRequest().getSession().getAttribute(key);
  }

  public String getRemoteAddr() {
    return this.getRequest().getRemoteAddr();
  }

  public void set(String key, Object value) {
    this.getRequest().setAttribute(key, value);
  }

  public Object get(String key) {
    return this.getRequest().getAttribute(key);
  }

  public Object getClaimsValue(String key) {
    Claims claims = (Claims)this.getRequest().getAttribute("claims");
    return claims != null && claims.get(key) != null ? claims.get(key) : null;
  }
}

那么在权限切面中首先需要获取出请求的用户信息jwt:用户信息的jwt字符串通常是通过请求头的Authorization来传递的,因为这个用户信息我们会经常用到,那么解析出来之后放到request的session中,注意是request的session,不是request.getSession()这个session,对于每个用户信息的生命周期只是这个request。getsession的这个session将会是全局的session,当然,前后端分离的项目中sessionid会变,所以这个不起作用的。

BaseController controller = (BaseController)joinPoint.getTarget();
    String token = controller.getRequest().getHeader("Authorization");
    if (token == null) {
      return joinPoint.proceed();
    } else {
      Claims claims = null;

      try {
        claims = TokenUtil.parse(token);
      } catch (ExpiredJwtException var6) {
        return ResultModel.builder().code("JWT EXPIRE").build();
      } catch (Exception var7) {
        return ResultModel.builder().code("JWT WRONG").build();
      }

      controller.set("claims", claims);
      return joinPoint.proceed();
    }

当权限不足的时候,返回内容的结构应该和controller层返回结构是一样的,统一采用一种对象:采用的是对象的构造器模式。

public class ResultModel {
  public static final String SUCCESS = "success";
  public static final String FAILED = "failed";
  public static final String ERROR = "error";
  private Object data;
  private String code;
  private String token;
  private int total;

  public ResultModel() {
  }
}

获取了用户的信息,那么我们就可以检验这个用户是否有权限访问这个接口啦,项目会有一张表来保存每个接口对每个权限的开放状态。如:

我用到了3张表,是因为满足数据库开发的3大范式,其实完全可以设置成一个表,表中存url和可以访问的权限(用‘,’分开)比如:

为了规范,还是建议用多张表!

切面就检查某个权限是否能访问某个接口:

public class AccessAspect {

	@Autowired
	private AuthorDao authorDao;

	public Object checkAccess(ProceedingJoinPoint joinPoint) throws Throwable {
		BaseController controller = (BaseController) joinPoint.getTarget();
		HttpServletRequest request = controller.getRequest();
		Integer roleId = (Integer) controller.getClaimsValue("roleId");
		roleId = roleId == null ? VISITOR : roleId;
		String api = request.getRequestURI().replaceAll(request.getContextPath(), "");
		while (api.contains("//")) {
			api = api.replace("//","/");
		}
		String methodName = request.getMethod();
		if ("OPTIONS".equals(methodName)) {
			return joinPoint.proceed();
		}
		String url = methodName + ":" + api;
		Author record = new Author(roleId, url);
		if (authorDao.verify(record)) {
			return joinPoint.proceed();
		} else {
			return ResultModel.builder().code(ILLEGAL_ACCESS_ERROR).build();
		}
	}
}

权限到就是一个查询对应表是否有记录:

@Repository
public class AuthorDaoImpl extends SqlSessionDaoSupport implements AuthorDao {

	private String getNamespace(){
		return "org.kelab.enterprise.dao.AuthorDao";
	}

	@Override
	public boolean verify(Author record) {
		return (Integer)getSqlSession().selectOne(getNamespace() + ".verify",record) > 0;
	}
}

sql代码:

<mapper namespace="org.kelab.enterprise.dao.AuthorDao">
    <select id="verify" resultType="java.lang.Integer" parameterType="org.kelab.enterprise.entity.Author">
        select count(*)
        from request_url re, role_request ro
        where re.id = ro.url_id
        and re.url = #{url,jdbcType=VARCHAR}
        and ro.role_id = #{roleId,jdbcType=INTEGER}
    </select>
</mapper>

猜你喜欢

转载自blog.csdn.net/qq_38089964/article/details/84584172
今日推荐