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