Java类:team.bangbang.common.filter.SSOFilter

该过滤器为权限过滤的入口。在该过滤器中,使用HttpServletRequest和HttpServletResponse构造权限处理的上下文SSOContext。

Java类:team.bangbang.sso.SSOContext

权限处理的上下文SSOContext里面包含了4个Java对象,分别是: 1. team.bangbang.sso.IApplicationSSO 实现类 应用系统对象 2. team.bangbang.sso.IAccountSSO 实现类 账户对象 3. team.bangbang.sso.IDataLimitSSO 实现类 数据权限对象 4. team.bangbang.sso.IFunctionLimitSSO 实现类 功能权限对象

以上4个Java对象会根据实际的业务需求,调用数据库或者接口获取相应的数据。

一、配置说明

# 单点登录
sso:
  # 应用系统对象
  application:
    id: 3
    # 类名,该类实现team.bangbang.sso.IApplicationSSO接口
    class: "cn.js.icode.sso.client.impl.XmgcglApplicationClient"
  account:
    # 类名,该类实现team.bangbang.sso.IAccountSSO接口
    class: "cn.js.icode.sso.client.impl.XmgcglAccountClient"
  data-limit:
    # 类名,该类实现team.bangbang.sso.IDataLimitSSO接口
    class: "cn.js.icode.sso.client.impl.XmgcglDataLimitClient"
  function-limit:
    # 类名,该类实现team.bangbang.sso.IFunctionLimitSSO接口
    class: "cn.js.icode.sso.client.impl.XmgcglFunctionLimitClient"
    # 权限免校验白名单目录,多个目录以半角逗号","间隔
    # 可以使用通配符 "*" 忽略所有目录校验
    # no-validation-modules: "/ckeditor_4.4.2/,/ajax/,/common/,/sample/"
    no-validation-modules: '/login,/index.html,/static/'
    # 权限免校验白名单地址,多个地址以半角逗号","间隔
    # 可以使用通配符 "*" 忽略所有地址校验,使用通配符时,等同于no_validation_modules使用通配符
    no-validation-urls: '/system/logon.do,/system/frame.do,/system/logout.do,/system/passwordUpdate.do,/login,/index.html,/static/,/microservice/client/ssoServer'
    # 校验不通过的登录地址
    login-url: "http://10.144.48.48:8081/oauth/index.html"

获取应用系统信息

在使用前后端分离的开发方式时,一般是由权限系统后端提供获取应用系统信息的接口,前端直接调用。获取的应用系统信息用于在统一的界面内构造不同子系统的入口。

获取账户信息

// 必须使用SSOFilter,然后才能使用SSOContext
// 获取的IAccountSSO对象,其实就是在配置文件中 sso.account.class 类实例化得到的对象
IAccountSSO accClt = SSOContext.getAccountSSO();
// 转换为Account对象,并添加dataLimit
Account user = (accClt != null ? accClt.getAccount() : null);

return user;

在spring框架下,为了更加便于获取账户信息,我们会使用注解。在AOP中扫描需要填充账户信息的注解,找到后获取账户信息,将账户信息填充到参数对象中:

public class CustomizedResolver implements HandlerMethodArgumentResolver {

    /*
     * (non-Javadoc)
     *
     * @see org.springframework.web.method.support.HandlerMethodArgumentResolver#
     * supportsParameter(org.springframework.core.MethodParameter)
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 使用SessionUser作为账户信息参数的注解
        boolean bl = parameter.hasParameterAnnotation(SessionUser.class);
        return bl;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // 处理SessionUser注解
        // 获取参数注解
        SessionUser su = parameter.getParameterAnnotation(SessionUser.class);
        if (su != null) {
            // 必须使用SSOFilter,然后才能使用SSOContext
            IAccountSSO accClt = SSOContext.getAccountSSO();
            // 转换为Account对象,并添加dataLimit
            Account user = (accClt != null ? accClt.getAccount() : null);

            return user;
        }

        return null;
    }
}

经过这样的处理,我们在Spring中就可以使用@SessionUser注解直接获得当前账户信息:

/**
 * 获取当前账号
 *
 * @param account 当前账户信息
 * @param request HTTP请求
 *
 * @return 返回结果
 */
@PostMapping("/account")
public ResponseBase getAccount(@SessionUser Account account, HttpServletRequest request) {
    DataResponse<Account> dr = new DataResponse<Account>();
    dr.setData(account);
    return dr;
}

获取数据权限

/**
 * 当前账号管理的组织范围
 *
 * @param applicationId 系统/资源编号
 * @param request HTTP请求
 *
 * @return 返回结果
 */
@PostMapping("/dataLimit")
public ResponseBase getDataLimit(String applicationId, HttpServletRequest request) {
    if (applicationId == null || applicationId.trim().length() == 0) {
        ResponseBase rb = new ResponseBase();
        rb.setStatusCode(StatusCode.REQUEST_DATA_EXPECTED);
        rb.setMessage("系统/资源编号 applicationId 未传入!");
        return rb;
    }

    DataResponse<DataLimit> dr = new DataResponse<DataLimit>();
    DataLimit dl = SSOContext.getDataLimitSSO().getDataLimit(applicationId);
    dr.setData(dl);
    return dr;
}

获取功能权限

/**
 * 检查当前是否有对指定权限编码、请求URI的访问权限
 *
 * @param applicationId 必填,系统/资源编号
 * @param code 选填,权限编码,code、uri二选一传入数据
 * @param uri 选填,请求URI,code、uri二选一传入数据
 * @param request HTTP请求,传递的参数包括:
 *        applicationId 必填,系统/资源编号
 *        code 选填,权限编码,code、uri二选一传入数据
 *        uri 选填,请求URI,code、uri二选一传入数据
 *
 * @return 返回结果
 */
@PostMapping("/canVisit")
public ResponseBase canVisit(String applicationId, String code, String uri, HttpServletRequest request) {
    DataResponse<Boolean> dr = new DataResponse<Boolean>();
    // 使用请求中的 applicationId、code/uri 参数调用sso server端接口,判断是否有权限
    boolean canVisit = SSOContext.getFunctionLimitSSO().canVisit(applicationId, code, uri);
    dr.setData(canVisit);
    if (!canVisit) {
        dr.setStatusCode(StatusCode.DATA_STATUS_ERROR);
        dr.setMessage("不允许访问");
    }

    return dr;
}