权限管理(三)自定义开发权限框架
首先是基于RBAC模型设计的库表;
开发基本的业务:
1、部门用户模块开发,添加更新展示部门模块,展示部门下用户,添加更新部门下用户。
2、角色模块:添加、更新角色,展示角色与权限树、当前角色下的用户,以及更新角色与权限,更新角色与用户;
3、权限模块:添加更新权限模块、添加更新模块下权限点,以及各自的展示。
登录处理:
思路添加过滤器filter:请求前获取session,查看是否有登录标记,没有返回登录页面,有则继续往下走。
@Slf4j
public class LoginFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
SysUser sysUser = (SysUser)req.getSession().getAttribute("user");
if (sysUser == null) {
String path = "/signin.jsp";
resp.sendRedirect(path);
return;
}
RequestHolder.add(sysUser);
RequestHolder.add(req);
filterChain.doFilter(servletRequest, servletResponse);
return;
}
public void destroy() {
}
}
权限过滤:
访问前过滤,拿到当前访问的uri,session中获取当前用户登录信息,通过用户查询当前用户的权限列表,如果含有该uri放行,
不含有该uri证明没有权限,拦截。
贴filter
@Slf4j
public class AclControlFilter implements Filter {
private static Set<String> exclusionUrlSet = Sets.newConcurrentHashSet();
private final static String noAuthUrl = "/sys/user/noAuth.page";
//初始化是为了拿到无需权限的uri
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// exclusionUrls已经在全局变量中配置了
String exclusionUrls = filterConfig.getInitParameter("exclusionUrls");
List<String> exclusionUrlList = Splitter.on(",").trimResults().omitEmptyStrings().splitToList(exclusionUrls);
exclusionUrlSet = Sets.newConcurrentHashSet(exclusionUrlList);
exclusionUrlSet.add(noAuthUrl);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String servletPath = request.getServletPath();
Map requestMap = request.getParameterMap();
if (exclusionUrlSet.contains(servletPath)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
SysUser sysUser = RequestHolder.getCurrentUser();
if (sysUser == null) {
log.info("someone visit {}, but no login, parameter:{}", servletPath, JsonMapper.obj2String(requestMap));
noAuth(request, response);
return;
}
SysCoreService sysCoreService = ApplicationContextHelper.popBean(SysCoreService.class);
if (!sysCoreService.hasUrlAcl(servletPath)) {
log.info("{} visit {}, but no login, parameter:{}", JsonMapper.obj2String(sysUser), servletPath, JsonMapper.obj2String(requestMap));
noAuth(request, response);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
return;
}
private void noAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {
String servletPath = request.getServletPath();
if (servletPath.endsWith(".json")) {
JsonData jsonData = JsonData.fail("没有访问权限,如需要访问,请联系管理员");
response.setHeader("Content-Type", "application/json");
response.getWriter().print(JsonMapper.obj2String(jsonData));
return;
} else {
clientRedirect(noAuthUrl, response);
return;
}
}
private void clientRedirect(String url, HttpServletResponse response) throws IOException{
response.setHeader("Content-Type", "text/html");
response.getWriter().print("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head>\n" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/>\n"
+ "<title>跳转中...</title>\n" + "</head>\n" + "<body>\n" + "跳转中,请稍候...\n" + "<script type=\"text/javascript\">//<![CDATA[\n"
+ "window.location.href='" + url + "?ret='+encodeURIComponent(window.location.href);\n" + "//]]></script>\n" + "</body>\n" + "</html>\n");
}
@Override
public void destroy() {
}
}
数据权限:
关于数据权限等,可能需要耦合到业务中开发,这样也带来弊端,维护困难,慎用。
关于ThreadLocal的使用:
/**
* @Auther: liuhy
* @Date: 2018/12/18 13:58
*/
public class RequestHolder {
private static final ThreadLocal<SysUser> userHolder = new ThreadLocal<>();
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<>();
public static void add(SysUser sysUser){
userHolder.set(sysUser);
}
public static void add(HttpServletRequest request){
requestHolder.set(request);
}
public static SysUser getCurrentUser(){
return userHolder.get();
}
public static HttpServletRequest getCurrentRequest(){
return requestHolder.get();
}
public static void remove(){
userHolder.remove();
requestHolder.remove();
}
}
ThreadLocal数据结构为map,key为当前线程,value即为我们存储的对象,线程安全。
用户存储登录信息方便易用,remove用来释放对象,一般放在拦截器的afterCompletion()方法中
实例如下:
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {
//请求前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return super.preHandle(request, response, handler);
}
//正常请求后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
//所有请求后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
log.info("request completed. 移除ThreadLocal");
RequestHolder.remove();
}
}