소스 검색

进一步的权限控制

Luxnk 7 년 전
부모
커밋
55904ade5a

+ 6 - 0
src/xyz/luxnk/lproject/MainSetup.java

@@ -1,6 +1,7 @@
 package xyz.luxnk.lproject;
 
 import org.apache.commons.mail.HtmlEmail;
+import org.nutz.dao.Cnd;
 import org.nutz.dao.Dao;
 import org.nutz.dao.util.Daos;
 import org.nutz.el.opt.custom.CustomMake;
@@ -9,6 +10,7 @@ import org.nutz.ioc.Ioc;
 import org.nutz.mvc.NutConfig;
 import org.nutz.mvc.Setup;
 import xyz.luxnk.lproject.bean.UserInfo;
+import xyz.luxnk.lproject.service.AuthorityService;
 import xyz.luxnk.lproject.service.UserService;
 import xyz.luxnk.lproject.util.SnowflakeIdWorker;
 
@@ -35,6 +37,10 @@ public class MainSetup implements Setup {
 
         // 获取NutQuartzCronJobFactory从而触发计划任务的初始化与启动
         ioc.get(NutQuartzCronJobFactory.class);
+
+        AuthorityService as = ioc.get(AuthorityService.class);
+        as.initFormPackage("xyz.luxnk.lproject");
+        as.checkBasicRoles(dao.fetch(UserInfo.class, Cnd.where("username", "=", "Luxnk")));
     }
 
     @Override

+ 328 - 0
src/xyz/luxnk/lproject/module/AuthorityModule.java

@@ -0,0 +1,328 @@
+package xyz.luxnk.lproject.module;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.authz.annotation.RequiresUser;
+import org.nutz.aop.interceptor.ioc.TransAop;
+import org.nutz.dao.Cnd;
+import org.nutz.dao.FieldFilter;
+import org.nutz.dao.pager.Pager;
+import org.nutz.dao.util.Daos;
+import org.nutz.ioc.aop.Aop;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.json.Json;
+import org.nutz.lang.Strings;
+import org.nutz.lang.util.NutMap;
+import org.nutz.mvc.Scope;
+import org.nutz.mvc.adaptor.JsonAdaptor;
+import org.nutz.mvc.annotation.*;
+import xyz.luxnk.lproject.bean.Permission;
+import xyz.luxnk.lproject.bean.Role;
+import xyz.luxnk.lproject.bean.UserInfo;
+import xyz.luxnk.lproject.bean.UserProfile;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 角色/权限管理
+ * 一个用户属于多种角色,拥有多种特许权限,每种角色拥有多种权限
+ */
+@At("/admin/authority")
+@IocBean
+@Ok("void") // 避免误写导致敏感信息泄露到服务器外
+public class AuthorityModule extends BaseModule {
+
+    @At("/")
+    @Ok("jsp:jsp.page.simple_role")
+    @RequiresUser
+    public void index() {}
+
+    /**************查询类***************/
+
+    /**
+     * 用户列表
+     * @param query
+     * @param pager
+     * @return
+     */
+    @RequiresPermissions("authority:user:query")
+    @At
+    @Ok("json: {locked: 'password|salt', ignoreNull: true}") // 禁止把password和salt字段进行传输
+    public Object users(@Param("query")String query, @Param("..")Pager pager) {
+        return ajaxOk(query(UserInfo.class, Cnd.NEW().asc("id"), pager, null));
+    }
+
+    /**
+     * 角色列表
+     * @param query
+     * @param pager
+     * @return
+     */
+    @RequiresPermissions("authority:role:query")
+    @At
+    @Ok("json")
+    public Object roles(@Param("query")String query, @Param("..")Pager pager) {
+        return ajaxOk(query(Role.class, Cnd.NEW().asc("id"), pager, null));
+    }
+
+    /**
+     * 权限列表
+     * @param query
+     * @param pager
+     * @return
+     */
+    @RequiresPermissions("authority:permission:query")
+    @At
+    @Ok("json")
+    public Object permissions(@Param("query")String query, @Param("..")Pager pager) {
+        return ajaxOk(query(Permission.class, Cnd.NEW().asc("id"), pager, null));
+    }
+
+    /**************用户操作***************/
+
+    /**
+     * 更新用户所属角色/特许权限
+     * @param user
+     * @param roles
+     * @param permissions
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:user:update")
+    @At("/user/update")
+    @Aop(TransAop.READ_COMMITTED)
+    public void updateUser(@Param("user")UserInfo user, @Param("roles")List<Long> roles, @Param("permissions")List<Long> permissions) {
+
+        // 防御一下
+        if (user == null)
+            return;
+        user = dao.fetch(UserInfo.class, user.getId());
+
+        // 就在那么一瞬间,那个用户已经被其他用户删掉了呢
+        if (user == null)
+            return;
+        if (roles != null) {
+            List<Role> rs = new ArrayList<Role>(roles.size());
+            for (long roleId : roles) {
+                Role r = dao.fetch(Role.class, roleId);
+                if (r != null) {
+                    rs.add(r);
+                }
+            }
+            dao.fetchLinks(user, "roles");
+            if (user.getRoles().size() > 0) {
+                dao.clearLinks(user, "roles");
+            }
+            user.setRoles(rs);
+            dao.insertRelation(user, "roles");
+        }
+        if (permissions != null) {
+            List<Permission> ps = new ArrayList<Permission>(permissions.size());
+            for (long permissionId : permissions) {
+                Permission p = dao.fetch(Permission.class, permissionId);
+                if (p != null) {
+                    ps.add(p);
+                }
+            }
+            dao.fetchLinks(user, "permissions");
+            if (user.getPermissions().size() > 0) {
+                dao.clearLinks(user, "permissions");
+            }
+            user.setPermissions(ps);
+            dao.insertRelation(user, "permissions");
+        }
+    }
+
+    /**
+     * 用于显示用户-权限修改对话框的信息
+     * @param id
+     * @return
+     */
+    @RequiresPermissions("authority:user:udpate")
+    @At("/user/fetch/permission")
+    @Ok("json")
+    public Object fetchUserPermissions(@Param("id")long id) {
+        UserInfo userInfo = dao.fetch(UserInfo.class, id);
+        if (userInfo == null)
+            return ajaxFail("not such user");
+        userInfo = dao.fetchLinks(userInfo, "permissions");
+        List<Permission> permissions = dao.query(Permission.class, Cnd.orderBy().asc("name"));
+        NutMap data = new NutMap();
+        data.put("user", userInfo);
+        data.put("permissions", permissions);
+        return ajaxOk(data);
+    }
+
+    /**
+     * 用于显示用户-角色修改对话框的信息
+     * @param id
+     * @return
+     */
+    @RequiresPermissions("authority:user:udpate")
+    @At("/user/fetch/role")
+    @Ok("json")
+    public Object fetchUserRoles(@Param("id")long id) {
+        UserInfo userInfo = dao.fetch(UserInfo.class, id);
+        if (userInfo == null)
+            return ajaxFail("not such user");
+        userInfo = dao.fetchLinks(userInfo, "roles");
+        List<Role> roles = dao.query(Role.class, Cnd.orderBy().asc("name"));
+        NutMap data = new NutMap();
+        data.put("user", userInfo);
+        data.put("permissions", roles);
+        return ajaxOk(data);
+    }
+
+    /**************角色操作***************/
+
+    /**
+     * 新增一个角色
+     * @param role
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:role:add")
+    @At("/role/add")
+    public void addRole(@Param("..")Role role) {
+        if (role == null)
+            return;
+        dao.insert(role);   // 注意,这里并没有没有用insertWith,即总是插入一个无权限角色
+    }
+
+    /**
+     * 删除一个角色,admin角色禁止删除
+     * @param role
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:role:delete")
+    @At("/role/delete")
+    public void delRole(@Param("..")Role role) {
+        if (role == null)
+            return;
+        role = dao.fetch(Role.class, role.getId());
+        if (role == null)
+            return;
+
+        // 不允许删除admin角色
+        if ("admin".equals(role.getName()))
+            return;
+        dao.delete(Role.class, role.getId());
+    }
+
+    /**
+     * 更新权限的一般信息或所拥有的权限
+     * @param role
+     * @param permissions
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:role:update")
+    @At("/role/update")
+    @Aop(TransAop.SERIALIZABLE)
+    public void updateRole(@Param("user")Role role, @Param("permissions")List<Long> permissions) {
+
+        // 防御一下
+        if (role == null)
+            return;
+        if (dao.fetch(Role.class, role.getId()) == null)
+            return;
+        if (!Strings.isBlank(role.getAlias()) || !Strings.isBlank(role.getDescription())) {
+            Daos.ext(dao, FieldFilter.create(Role.class, "alias|desc")).update(role);
+        }
+        if (permissions != null) {
+            List<Permission> ps = new ArrayList<Permission>();
+            for (long permissionId : permissions) {
+                Permission p = dao.fetch(Permission.class, permissionId);
+                if (p != null) {
+                    ps.add(p);
+                }
+            }
+
+            // 如果有老的权限,先清空,后插入新的纪录
+            dao.fetchLinks(role, "permissions");
+            if (role.getPermissions().size() > 0) {
+                dao.clearLinks(role, "permissions");
+            }
+            role.setPermissions(ps);
+            dao.insertRelation(role, "permissions");
+        }
+    }
+
+    /**
+     * 用于显示角色-权限修改对话框的信息
+     * @param id
+     * @return
+     */
+    @RequiresPermissions("authority:role:update")
+    @At("/role/fetch")
+    @Ok("json")
+    public Object fetchRolePermissions(@Param("id")long id) {
+        Role role = dao.fetch(Role.class, id);
+        if (role == null)
+            return ajaxFail("not such role");
+        role = dao.fetchLinks(role, null);
+        List<Permission> permissions = dao.query(Permission.class, Cnd.orderBy().asc("name"));
+        NutMap data = new NutMap();
+        data.put("role", role);
+        data.put("permissions", permissions);
+        return ajaxOk(data);
+    }
+
+    /**************权限操作***************/
+
+    /**
+     * 新增一个权限
+     * @param permission
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:permission:add")
+    @At("/permission/add")
+    public void addRole(@Param("..")Permission permission) {
+        if (permission == null)
+            return;
+        dao.insert(permission);
+    }
+
+    /**
+     * 删除一个权限,authority相关权限禁止删除
+     * @param permission
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:permission:delete")
+    @At("/permission/delete")
+    public void delRole(@Param("..")Permission permission) {
+        if (permission == null)
+            return;
+        permission = dao.fetch(Permission.class, permission.getId());
+        if (permission == null)
+            return;
+
+        // 不允许删除authority相关权限
+        if (permission.getName().contains("authority"))
+            return;
+        dao.delete(Permission.class, permission.getId());
+    }
+
+    /**
+     * 修改权限的一般信息
+     * @param permission
+     */
+    @POST
+    @AdaptBy(type = JsonAdaptor.class)
+    @RequiresPermissions("authority:permission:delete")
+    @At("/permission/update")
+    public void updateRole(@Param("..")Permission permission) {
+        if (permission == null)
+            return;
+        if (dao.fetch(Permission.class, permission.getId()) == null)
+            return;
+        permission.setUpdateTime(new Date());
+        permission.setCreateTime(null);
+        Daos.ext(dao, FieldFilter.create(Permission.class, null, "name", true)).update(permission);
+    }
+
+}

+ 24 - 0
src/xyz/luxnk/lproject/module/BaseModule.java

@@ -1,10 +1,16 @@
 package xyz.luxnk.lproject.module;
 
+import org.nutz.dao.Condition;
 import org.nutz.dao.Dao;
+import org.nutz.dao.QueryResult;
+import org.nutz.dao.pager.Pager;
 import org.nutz.ioc.loader.annotation.Inject;
 import org.nutz.lang.random.R;
+import org.nutz.lang.util.NutMap;
 import xyz.luxnk.lproject.service.EmailService;
 
+import java.util.List;
+
 public abstract class BaseModule {
 
     @Inject // 注入同名的一个ioc对象
@@ -15,4 +21,22 @@ public abstract class BaseModule {
 
     protected byte[] emailKEY = R.sg(24).next().getBytes();
 
+    protected QueryResult query(Class<?> klass, Condition cnd, Pager pager, String regex) {
+        if (pager != null && pager.getPageNumber() < 1) {
+            pager.setPageNumber(1);
+        }
+        List<?> roles = dao.query(klass, cnd, pager);
+        dao.fetchLinks(roles, null);
+        pager.setRecordCount(dao.count(klass, cnd));
+        return new QueryResult(roles, pager);
+    }
+
+    protected NutMap ajaxOk(Object data) {
+        return new NutMap().setv("ok", true).setv("data", data);
+    }
+
+    protected NutMap ajaxFail(String msg) {
+        return new NutMap().setv("ok", false).setv("msg", msg);
+    }
+
 }

+ 4 - 3
src/xyz/luxnk/lproject/module/UserModule.java

@@ -1,6 +1,7 @@
 package xyz.luxnk.lproject.module;
 
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.apache.shiro.authz.annotation.RequiresUser;
 import org.nutz.aop.interceptor.ioc.TransAop;
 import org.nutz.dao.Cnd;
@@ -133,7 +134,7 @@ public class UserModule extends BaseModule {
      * @return
      */
     @At
-    @RequiresUser
+    @RequiresPermissions("user:add")
     public Object add(@Param("..")UserInfo userInfo) {  // 两个点号是表示按对象属性一一设置
         NutMap re = new NutMap();
         String msg = checkUser(userInfo, true);
@@ -151,7 +152,7 @@ public class UserModule extends BaseModule {
      * @return
      */
     @At
-    @RequiresUser
+    @RequiresPermissions("user:update")
     public Object update(@Param("password")String password, @Attr("me")String me) {
         NutMap re = new NutMap();
         if (Strings.isBlank(password) || password.length() < 6) {
@@ -169,7 +170,7 @@ public class UserModule extends BaseModule {
      */
     @At
     @Aop(TransAop.READ_COMMITTED)
-    @RequiresUser
+    @RequiresPermissions("user:delete")
     public Object delete(@Param("id")String id, @Attr("me")String me) {
         if (me.equals(id)) {
             return new NutMap().setv("ok", false).setv("msg", "不能删除当前用户!");

+ 33 - 0
src/xyz/luxnk/lproject/service/AuthorityService.java

@@ -0,0 +1,33 @@
+package xyz.luxnk.lproject.service;
+
+import xyz.luxnk.lproject.bean.Role;
+import xyz.luxnk.lproject.bean.UserInfo;
+
+public interface AuthorityService {
+
+    /**
+     * 扫描RequiresPermissions和RequiresRoles注解
+     * @param pkg 需要扫描的package
+     */
+    void initFormPackage(String pkg);
+
+    /**
+     * 检查最基础的权限,确保admin用户-admin角色-(用户增删改查-权限增删改查)这一基础权限设置
+     * @param admin
+     */
+    void checkBasicRoles(UserInfo admin);
+
+    /**
+     * 添加一个权限
+     * @param permission
+     */
+    void addPermission(String permission);
+
+    /**
+     * 添加一个角色
+     * @param role
+     * @return
+     */
+    Role addRole(String role);
+
+}

+ 126 - 0
src/xyz/luxnk/lproject/service/AuthorityServiceImpl.java

@@ -0,0 +1,126 @@
+package xyz.luxnk.lproject.service;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.nutz.dao.Chain;
+import org.nutz.dao.Cnd;
+import org.nutz.dao.Dao;
+import org.nutz.dao.entity.Record;
+import org.nutz.ioc.loader.annotation.Inject;
+import org.nutz.ioc.loader.annotation.IocBean;
+import org.nutz.lang.ContinueLoop;
+import org.nutz.lang.Each;
+import org.nutz.lang.ExitLoop;
+import org.nutz.lang.LoopException;
+import org.nutz.log.Log;
+import org.nutz.log.Logs;
+import org.nutz.resource.Scans;
+import xyz.luxnk.lproject.bean.Permission;
+import xyz.luxnk.lproject.bean.Role;
+import xyz.luxnk.lproject.bean.UserInfo;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@IocBean(name = "authorityService")
+public class AuthorityServiceImpl implements AuthorityService {
+
+    private static final Log log = Logs.get();
+
+    @Inject
+    Dao dao;
+
+    @Override
+    public void initFormPackage(String pkg) {
+
+        // 搜索@RequiresPermissions注解,初始化权限表
+        // 搜索@RequiresRoles注解,初始化角色表
+        final Set<String> permissions = new HashSet<String>();
+        final Set<String> roles = new HashSet<String>();
+        for (Class<?> klass : Scans.me().scanPackage(pkg)) {
+            for (Method method : klass.getMethods()) {
+                RequiresPermissions rp = method.getAnnotation(RequiresPermissions.class);
+                if (rp != null && rp.value() != null) {
+                    for (String permission : rp.value()) {
+                        if (permission != null && !permission.endsWith("*"))
+                            permissions.add(permission);
+                    }
+                }
+                RequiresRoles rr = method.getAnnotation(RequiresRoles.class);
+                if (rr != null && rr.value() != null) {
+                    for (String role : rr.value()) {
+                        roles.add(role);
+                    }
+                }
+            }
+        }
+        log.debugf("found %d permission", permissions.size());
+        log.debugf("found %d role", roles.size());
+
+        // 把全部权限查出来一一检查
+        dao.each(Permission.class, null, new Each<Permission>() {
+            @Override
+            public void invoke(int index, Permission ele, int length) throws ExitLoop, ContinueLoop, LoopException {
+                permissions.remove(ele.getName());
+            }
+        });
+        dao.each(Role.class, null, new Each<Role>() {
+            @Override
+            public void invoke(int index, Role ele, int length) throws ExitLoop, ContinueLoop, LoopException {
+                roles.remove(ele.getName());
+            }
+        });
+        for (String permission : permissions) {
+            addPermission(permission);
+        }
+        for (String role : roles) {
+            addRole(role);
+        }
+    }
+
+    @Override
+    public void checkBasicRoles(UserInfo admin) {
+
+        // 检查一下admin的权限
+        Role adminRole = dao.fetch(Role.class, "admin");
+        if (adminRole == null) {
+            adminRole = addRole("admin");
+        }
+
+        // admin账号必须存在于admin组
+        if (0 == dao.count("relation_user_role", Cnd.where("u_id", "=", admin.getId()).and("role_id", "=", adminRole.getId()))) {
+            dao.insert("relation_user_role", Chain.make("u_id", admin.getId()).add("role_id", adminRole.getId()));
+        }
+
+        // admin账号必须有authority:* 也就是权限管理相关的权限
+        List<Record> res = dao.query("relation_role_permission", Cnd.where("role_id", "=", adminRole.getId()));
+        OUT: for (Permission permission : dao.query(Permission.class, Cnd.where("name", "like", "authority:%").or("name", "like", "user:%").or("name", "like", "topic:%"), null)) {
+            for (Record re : res) {
+                if (re.getInt("permission_id") == permission.getId())
+                    continue OUT;
+            }
+            dao.insert("relation_role_permission", Chain.make("role_id", adminRole.getId()).add("permission_id", permission.getId()));
+        }
+    }
+
+    @Override
+    public void addPermission(String permission) {
+        Permission p = new Permission();
+        p.setName(permission);
+        p.setUpdateTime(new Date());
+        p.setCreateTime(new Date());
+        dao.insert(p);
+    }
+
+    @Override
+    public Role addRole(String role) {
+        Role r = new Role();
+        r.setName(role);
+        r.setUpdateTime(new Date());
+        r.setCreateTime(new Date());
+        return dao.insert(r);
+    }
+}

+ 690 - 0
web/WEB-INF/jsp/page/authority.jsp

@@ -0,0 +1,690 @@
+<%--
+  Created by IntelliJ IDEA.
+  User: Administrator
+  Date: 2018/4/12 0012
+  Time: 15:21
+  To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+<!-- 用bootstrap先应付一下 -->
+<!-- Latest compiled and minified CSS -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
+<!-- Optional theme -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css">
+<!-- Latest compiled and minified JavaScript -->
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
+
+<div class="row">
+    <ul class="md-tab-group">
+        <li>
+            <div class="ripple-button" id="_user_role" onclick="showTab('user_role')">用户权限一览</div>
+        </li>
+        <li>
+            <div class="ripple-button" id="_roles" onclick="showTab('roles')">角色一览</div>
+        </li>
+        <li>
+            <div class="ripple-button" id="_permissions" onclick="showTab('permissions')">权限一览</div>
+        </li>
+        <div class="tab-button"></div>
+    </ul>
+</div>
+
+<div>
+    <!-- 用户列表 -->
+    <div id="user_role">
+        <h2>用户管理</h2>
+        <p id="user_count"></p>
+        <form action="#" id="user_query">
+            用户过滤<input name="query">
+            页数<input name="pageNumber" type="number" value="1" onchange="loadUsers();">
+            每页大小<input name="pageSize" type="number" value="10" onchange="loadUsers();">
+        </form>
+
+        <div class="panel panel-default">
+            <div class="panel-heading">用户一览</div>
+            <table class="table">
+                <thead>
+                <tr>
+                    <th>#</th>
+                    <th>名称</th>
+                    <th>别称</th>
+                    <th>角色</th>
+                    <th>特许权限</th>
+                    <th>操作</th>
+                </tr>
+                </thead>
+                <tbody id="user_list"></tbody>
+            </table>
+        </div>
+    </div>
+
+
+
+    <!-- 角色列表 -->
+    <div id="roles">
+        <h2>角色管理</h2>
+        <p id="role_count"></p>
+        <form action="#" id="role_query">
+            角色过滤<input name="query">
+            页数<input name="pageNumber" type="number" value="1" onchange="loadRoles();">
+            每页大小<input name="pageSize" type="number" value="10" onchange="loadRoles();">
+        </form>
+        <div>
+
+        </div>
+        <button onclick="role_add();" class="btn btn-default">新增角色</button>
+        <div class="panel panel-default">
+            <div class="panel-heading">角色一览</div>
+            <table class="table">
+                <thead>
+                <tr>
+                    <th>#</th>
+                    <th>角色</th>
+                    <th>别称</th>
+                    <th>描述</th>
+                    <th>权限</th>
+                    <th>操作</th>
+                </tr>
+                </thead>
+                <tbody id="role_list"></tbody>
+            </table>
+        </div>
+    </div>
+    <!-- 权限列表 -->
+    <div id="permissions">
+        <h2>权限管理</h2>
+        <p id="permission_count"></p>
+        <form action="#" id="permission_query">
+            权限过滤<input name="query">
+            页数<input name="pageNumber" type="number" value="1" onchange="loadPermissions();">
+            每页大小<input name="pageSize" type="number" value="10" onchange="loadPermissions();">
+        </form>
+        <button onclick="permission_add();" class="btn btn-default">新增</button>
+
+        <div class="panel panel-default">
+            <div class="panel-heading">权限一览</div>
+            <table class="table">
+                <thead>
+                <tr>
+                    <th>#</th>
+                    <th>权限</th>
+                    <th>别称</th>
+                    <th>描述</th>
+                    <th>操作</th>
+                </tr>
+                </thead>
+                <tbody id="permission_list"></tbody>
+            </table>
+        </div>
+    </div>
+</div>
+
+<!-- 各种默认隐藏的div -->
+<!-- 修改用户的特许权限 -->
+<div style="display: none;" id="user_permission_modify">
+    <p>
+        <input id="user_permission_modify_id" value="" hidden="true">
+        <input id="user_permission_modify_name" value="" hidden="true">
+    </p>
+    <form action="#">
+        <div id="user_permissions_div"></div>
+    </form>
+    <button onclick="user_permission_modify_submit();">提交</button>
+</div>
+<!-- 修改用户所属的角色 -->
+<div style="display: none;" id="user_role_modify">
+    <p>
+        <input id="user_role_modify_id" value="" hidden="true">
+        <input id="user_role_modify_name" value="" hidden="true">
+    </p>
+    <form action="#">
+        <div id="user_roles_div"></div>
+    </form>
+    <button onclick="user_role_modify_submit();">提交</button>
+</div>
+<!-- 修改角色所拥有的权限 -->
+<div style="display: none;" id="role_permission_modify">
+    <p>
+        <input id="role_permission_modify_id" value="" hidden="true">
+        <input id="role_permission_modify_name" value="" hidden="true">
+    </p>
+    <form action="#">
+        <div id="role_permissions_div"></div>
+    </form>
+    <button onclick="role_permission_modify_submit();">提交</button>
+</div>
+<!-- 修改角色的一般属性,如别称,描述 -->
+<div style="display: none;" id="role_modify">
+    <div style="display: none;">
+        <input name="role_modify_id" id="role_modify_id">
+    </div>
+    <div>
+        <div class="md-text-field has-float-label">
+            <label>${msg['authority.role.alias']}</label>
+            <input type="text" id="role_modify_alias" name="alias" nm="${msg['authority.role.alias']}" value="">
+            <hr class="underline">
+            <hr class="underline-focus">
+            <div class="err-tip">别名不能为空</div>
+        </div>
+    </div>
+    <div>
+        <div class="md-text-field has-float-label">
+            <label>${msg['authority.role.description']}</label>
+            <input type="text" id="role_modify_description" name="description" nm="${msg['authority.role.description']}" value="">
+            <hr class="underline">
+            <hr class="underline-focus">
+            <div class="err-tip">描述不能为空</div>
+        </div>
+    </div>
+    <div>
+        <button class="md-button raised-button is-primary" type="submit" onclick="role_update_submit();">
+            <span class="md-button-label">修改</span>
+        </button>
+        <button class="md-button raised-button is-primary" type="submit" onclick="$('#role_modify').hide();">
+            <span class="md-button-label">取消</span>
+        </button>
+    </div>
+</div>
+<!-- 修改权限的一般属性 -->
+<div style="display: none;" id="permission_modify">
+    <div style="display: none;">
+        <input name="permission_modify_id" id="permission_modify_id">
+    </div>
+    <div>
+        <div class="md-text-field has-float-label">
+            <label>${msg['authority.permission.alias']}</label>
+            <input type="text" id="permission_modify_alias" name="alias" nm="${msg['authority.permission.alias']}" value="">
+            <hr class="underline">
+            <hr class="underline-focus">
+            <div class="err-tip">别名不能为空</div>
+        </div>
+    </div>
+    <div>
+        <div class="md-text-field has-float-label">
+            <label>${msg['authority.permission.description']}</label>
+            <input type="text" id="permission_modify_description" name="description" nm="${msg['authority.permission.description']}" value="">
+            <hr class="underline">
+            <hr class="underline-focus">
+            <div class="err-tip">描述不能为空</div>
+        </div>
+    </div>
+    <div>
+        <button class="md-button raised-button is-primary" type="submit" onclick="permission_update_submit();">
+            <span class="md-button-label">修改</span>
+        </button>
+        <button class="md-button raised-button is-primary" type="submit" onclick="$('#permission_modify').hide();">
+            <span class="md-button-label">取消</span>
+        </button>
+    </div>
+</div>
+
+<script>
+    _r = {};
+
+    /*页面片段的初始化方法*/
+    function myInit(args) {
+
+        loadUsers();
+        loadRoles();
+        loadPermissions();
+        $('#_user_role').click();
+
+    }
+
+    /**
+     * Tab标签卡切换
+     */
+    function showTab(tab) {
+        $('.ripple-button').each(function (_, z) {
+            z = $(z);
+            tabId = z.attr('id');
+            if (tabId == undefined)
+                return;
+            tabId = tabId.substring(1);
+            TAB = $('#' + tabId);
+            if (tabId == tab) {
+                TAB.show();
+            } else {
+                TAB.hide();
+            }
+        })
+    }
+
+    function loadUsers() {
+        $.ajax({
+            url: home_base + '/admin/authority/users',
+            data: $('#user_query').serialize(),
+            dataType: 'json',
+            type: 'POST',
+            success: function (re) {
+                if (!re.ok) {
+                    alert(re.msg);
+                    return;
+                }
+                data = re.data;
+                $('#user_count').html('共' + data.pager.recordCount + '个用户,总计' + data.pager.pagerCount + '页');
+                var list_html = '';
+                for (var i = 0; i < data.list.length; i++) {
+                    var user = data.list[i];
+                    var pstr = '';
+                    for (var j = 0; j < user.permissions.length; j++) {
+                        var p = user.permissions[j];
+                        if (p.alias)
+                            pstr += p.alias;
+                        else
+                            pstr += p.name;
+                        pstr += ' ';
+                        if (j > 4 && user.permissions.length > 4) {
+                            pstr += '等' + user.permissions.length + '种权限';
+                            break;
+                        }
+                    }
+                    var rstr = '';
+                    for (var j = 0; j < user.roles.length; j++) {
+                        var r = user.roles[j];
+                        if (r.alias)
+                            rstr += r.alias;
+                        else
+                            rstr += r.name;
+                        rstr += ' ';
+                        if (j > 4 && user.roles.length > 4) {
+                            rstr += '等' + user.roles.length + '种角色';
+                            break;
+                        }
+                    }
+                    var tmp = '\n<tr>'
+                        + '<td>' + user.id + '</td>'
+                        + '<td>' + user.name + '</td>'
+                        + '<td>' + (user.alias ? user.alias : '') + '</td>'
+                        + '<td>' + rstr + '</td>'
+                        + '<td>' + pstr + '</td>'
+                        + '<td>'
+                        + ' <button onclick="user_role_update("' + user.id + '");" class="btn btn-default">修改角色</button>'
+                        + ' <button onclick="user_permission_update("' + user.id + '");" class="btn btn-default">修改特许权限</button>'
+                        + '</td>'
+                        + '</tr>'
+                    list_html += tmp;
+                }
+                $('#user_list').html(list_html);
+                _r._user_roles = data;
+            }
+        });
+    }
+
+    function loadRoles() {
+        $.ajax({
+            url: home_base + '/admin/authority/roles',
+            data: $('#role_query').serialize(),
+            dataType: 'json',
+            type: 'POST',
+            success: function (re) {
+                if (!re.ok) {
+                    alert(re.msg);
+                    return;
+                }
+                data = re.data;
+                $('#role_count').html('共' + data.pager.recordCount + '个角色,总计' + data.pager.pagerCount + '页');
+                var list_html = '';
+                for (var i = 0; i < data.list.length; i++) {
+                    var role = data.list[i];
+                    var pstr = '';
+                    for (var j = 0; j < role.permissions.length; j++) {
+                        var p = role.permissions[j];
+                        if (p.alias)
+                            pstr += p.alias;
+                        else
+                            pstr += p.name;
+                        pstr += ' ';
+                        if (j > 4 && role.permissions.length > 4) {
+                            pstr += '等' + role.permissions.length + '种权限';
+                            break;
+                        }
+                    }
+                    var tmp = '\n<tr>'
+                        + '<td>' + role.id + '</td>'
+                        + '<td>' + role.name + '</td>'
+                        + '<td>' + (role.alias ? role.alias : '') + '</td>'
+                        + '<td></td>'
+                        + '<td>' + pstr + '</td>'
+                        + '<td>'
+                        + ' <button onclick="role_update(' + role.id + ');" class="btn btn-default">修改描述</button>'
+                        + ' <button onclick="role_permission_update(' + role.id + ');" class="btn btn-default">修改权限</button>'
+                        + ' <button onclick="role_delete(' + role.id + ');" class="btn btn-default">删除</button>'
+                        + '</td>'
+                        + '</tr>'
+                    list_html += tmp;
+                }
+                $('#role_list').html(list_html);
+                _r._roles = data;
+            }
+        });
+    }
+
+    function loadPermissions() {
+        $.ajax({
+            url: home_base + '/admin/authority/permissions',
+            data: $('#permission_query').serialize(),
+            dataType: 'json',
+            type: 'POST',
+            success: function (re) {
+                if (!re.ok) {
+                    alert(re.msg);
+                    return;
+                }
+                data = re.data;
+                $('#permission_count').html('共' + data.pager.recordCount + '个权限,总计' + data.pager.pagerCount + '页');
+                var list_html = '';
+                for (var i = 0; i < data.list.length; i++) {
+                    var permission = data.list[i];
+                    var tmp = '\n<tr>'
+                        + '<td>' + permission.id + '</td>'
+                        + '<td>' + permission.name + '</td>'
+                        + '<td>' + (permission.alias ? permission.alias : '') + '</td>'
+                        + '<td>' + (permission.description ? permission.description : '') + '</td>'
+                        + '<td>'
+                        + ' <button onclick="permission_update(' + permission.id + ');" class="btn btn-default">修改</button>'
+                        + ' <button onclick="permission_delete(' + permission.id + ');" class="btn btn-default">删除</button>'
+                        + '</td>'
+                        + '</tr>'
+                    list_html += tmp;
+                }
+                $('#permission_list').html(list_html);
+                _r._roles = data;
+            }
+        });
+    }
+
+    // 各种操作哦哦哦哦哦哦哦哦哦
+
+    /*更新用户特许权限*/
+    function user_permission_update(user_id) {
+        $.ajax({
+            url: home_base + '/admin/authority/user/fetch/permission',
+            dataType: 'json',
+            data: {id: user_id},
+            success: function (re) {
+                if (re && re.ok) {
+                    var html = '';
+                    var ps = re.data.permissions;
+                    for (var i = 0; i < ps.length; i++) {
+                        var p = ps[i];
+                        var flag = false;
+                        for (var j = 0; j < re.data.user.permissions.length; j++) {
+                            if (re.data.user.permissions[j].id == p.id) {
+                                flag = true;
+                                break;
+                            }
+                        }
+                        if (p.alias) {
+                            html += p.alias;
+                        } else {
+                            html += p.name;
+                        }
+                        if (flag) {
+                            html += '<input type="checkbox" t="checkbox_user_permission" pid="' + p.id + '" checked="true" />\n'
+                        } else {
+                            html += '<input type="checkbox" t="checkbox_user_permission" pid="' + p.id + '" />\n'
+                        }
+                    }
+                    $('#user_permission_modify_id').val('' + user_id);
+                    $('#user_permission_modify_name').val('' + re.data.user.name);
+                    $('#user_permissions_div').html(html);
+                    $('#user_permission_modify').show();
+                }
+            }
+        });
+    }
+
+    /*将用户的特许权限提交到服务器*/
+    function user_permission_modify_submit() {
+        var user_id = $('#user_permission_modify_id').val();
+        var ps = $('input[t="checkbox_user_permission"]:checked');
+        var pids = [];
+        ps.each(function (i, p_input) {
+            pids.push($(p_input).attr('pid'));
+        });
+        $.ajax({
+            url: home_base + '/admin/authority/user/update',
+            type: 'POST',
+            data: JSON.stringify({'user': {id: user_id}, 'permissions': pids}),
+            success: function () {
+                loadUsers();
+                $('#user_permission_modify').hide();
+            }
+        })
+    }
+    
+    function user_role_update(user_id) {
+        $.ajax({
+            url: home_base + '/admin/authority/user/fetch/role',
+            dataType: 'json',
+            data: {id: user_id},
+            success: function (re) {
+                if (re && re.ok) {
+                    var html = '';
+                    var rs = re.data.permissions;
+                    for (var i = 0; i < rs.length; i++) {
+                        var r = rs[i];
+                        var flag = false;
+                        for (var j = 0; j < re.data.user.roles.length; j++) {
+                            if (re.data.user.roles[j].id == r.id) {
+                                flag = true;
+                                break;
+                            }
+                        }
+                        if (r.alias) {
+                            html += r.alias;
+                        } else {
+                            html += r.name;
+                        }
+                        if (flag) {
+                            html += '<input type="checkbox" t="checkbox_user_role" rid="' + r.id + '" checked="true" />\n'
+                        } else {
+                            html += '<input type="checkbox" t="checkbox_user_role" rid="' + r.id + '" />\n'
+                        }
+                    }
+                    $('#user_role_modify_id').val('' + user_id);
+                    $('#user_role_modify_name').val('' + re.data.user.name);
+                    $('#user_roles_div').html(html);
+                    $('#user_role_modify').show();
+                }
+            }
+        });
+    }
+
+    function user_role_modify_submit() {
+        var user_id = $('#user_role_modify_id').val();
+        var rs = $('input[t="checkbox_user_role"]:checked');
+        var rids = [];
+        rs.each(function (i, r_input) {
+            rids.push($(r_input).attr('rid'));
+        });
+        $.ajax({
+            url: home_base + '/admin/authority/user/update',
+            type: 'POST',
+            data: JSON.stringify({'user': {id: user_id}, 'roles': rids}),
+            success: function () {
+                loadUsers();
+                $('#user_role_modify').hide();
+            }
+        })
+    }
+
+    // 角色相关操作
+
+    function role_update(role_id) {
+        for (var i = 0; i < _r._roles.list.length; i++) {
+            var role = _r._roles.list[i];
+            if (role.id == role_id) {
+                $('#role_modify_id').attr('value', role_id);
+                $('#role_modify_alias').attr('value', role.alias);
+                $('#role_modify_description').attr('value', role.description);
+                $('#role_modify').show();
+                return;
+            }
+        }
+    }
+
+    /*将角色的一般属性的修改发送到服务器进行修改*/
+    function role_update_submit() {
+        var role_id = $("#role_modify_id").val();
+        var p = {id:role_id};
+        p["alias"] = $("#role_modify_alias").val();
+        p["description"] = $("#role_modify_description").val();
+        console.log(p);
+        $.ajax({
+            url : home_base + "/admin/authority/role/update",
+            type : "POST",
+            data : JSON.stringify({"role":p}),
+            success : function() {
+                loadRoles();
+                $("#role_modify").hide();
+            }
+            // TODO 处理异常
+        });
+    }
+    /*删除一个角色*/
+    function role_delete(role_id) {
+        // TODO 提示用户确认
+        $.ajax({
+            url : home_base + "/admin/authority/role/delete",
+            type : "POST",
+            data : JSON.stringify({id:role_id})
+        });
+        loadRoles();
+    }
+    /*新增角色*/
+    function role_add() {
+        var role_name = prompt("请输入新角色的名称,仅限英文字母,长度3到10个字符");
+        var re = /[a-zA-Z]{3,10}/;
+        if (role_name && re.exec(role_name)) {
+            $.ajax({
+                url : home_base + "/admin/authority/role/add",
+                type : "POST",
+                data : JSON.stringify({name:role_name}),
+                success : function () {
+                    loadRoles();
+                }
+            });
+        }
+    }
+    /*更新角色所拥有的权限列表,这个方法用于显示多选框*/
+    function role_permission_update(role_id) {
+        $.ajax({
+            url : home_base + "/admin/authority/role/fetch",
+            dataType : "json",
+            data : {id:role_id},
+            success : function (re) {
+                if (re && re.ok) {
+                    console.log(re.data);
+                    var html = "";
+                    var ps = re.data.permissions;
+                    for (var i = 0; i < ps.length; i++) {
+                        var p = ps[i];
+                        var flag = false;
+                        for (var j = 0; j < re.data.role.permissions.length; j++) {
+                            if (re.data.role.permissions[j].id == p.id) {
+                                flag = true;
+                                break;
+                            }
+                        }
+                        if (p.alias) {
+                            html += p.alias;
+                        } else {
+                            html += p.name;
+                        }
+                        if (flag) {
+                            html += "<input type='checkbox' t='checkbox_role_permission' pid='" + p.id + "' checked='true'>\n"
+                        } else {
+                            html += "<input type='checkbox' t='checkbox_role_permission' pid='" + p.id + "'>\n"
+                        }
+                    }
+                    $("#role_permission_modify_id").val(""+role_id);
+                    $("#role_permission_modify_name").val(""+re.data.role.name);
+                    $("#role_permissions_div").html(html);
+                    $("#role_permission_modify").show();
+                }
+            }
+        });
+    }
+    /*将角色的权限列表提交到服务器去*/
+    function role_permission_modify_submit() {
+        var role_id = $("#role_permission_modify_id").val();
+        var ps = $("input[t='checkbox_role_permission']:checked");
+        console.log(ps);
+        console.log(role_id);
+        var pids = [];
+        ps.each(function(i, p_input) {
+            pids.push($(p_input).attr("pid"));
+        });
+        $.ajax({
+            url : home_base + "/admin/authority/role/update",
+            type : "POST",
+            data : JSON.stringify({"role":{id:role_id}, "permissions":pids}),
+            success : function() {
+                loadRoles();
+                $("#role_permission_modify").hide();
+            }
+        });
+    }
+    /*更新权限的一般信息,显示输入框*/
+    function permission_update(permission_id) {
+        for (var i=0;i<_r._permissions.list.length;i++) {
+            var permission = _r._permissions.list[i];
+            if (permission.id == permission_id) {
+                $("#permission_modify_id").attr("value", permission_id);
+                $("#permission_modify_alias").attr("value", permission.alias);
+                $("#permission_modify_description").attr("value", permission.description);
+                $("#permission_modify").show();
+                return;
+            }
+        }
+    }
+    /*把权限的一般信息的修改提交到服务器*/
+    function permission_update_submit() {
+        var permission_id = $("#permission_modify_id").val();
+        var p = {id:permission_id};
+        p["alias"] = $("#permission_modify_alias").val();
+        p["description"] = $("#permission_modify_description").val();
+        console.log(p);
+        $.ajax({
+            url : home_base + "/admin/authority/permission/update",
+            type : "POST",
+            data : JSON.stringify(p),
+            success : function() {
+                loadPermissions();
+                $("#permission_modify").hide();
+            }
+        });
+    }
+    /*删除一个权限*/
+    function permission_delete(permission_id) {
+        $.ajax({
+            url : home_base + "/admin/authority/permission/delete",
+            type : "POST",
+            data : JSON.stringify({id:permission_id}),
+            success : function() {
+                loadPermissions();
+                $("#permission_modify").hide();
+            }
+        });
+        loadPermissions();
+    }
+    /*新增一个权限*/
+    function permission_add() {
+        var permission_name = prompt("请输入新角色的名词,仅限英文字母/冒号/米号,长度3到30个字符");
+        var re = /[a-zA-Z\:\*]{3,10}/;
+        if (permission_name && re.exec(permission_name)) {
+            $.ajax({
+                url : home_base + "/admin/authority/permission/add",
+                type : "POST",
+                data : JSON.stringify({name:permission_name}),
+                success : function () {
+                    loadPermissions();
+                }
+            });
+        }
+    }
+
+</script>

+ 23 - 0
web/WEB-INF/jsp/page/simple_role.jsp

@@ -0,0 +1,23 @@
+<%--
+  Created by IntelliJ IDEA.
+  User: Administrator
+  Date: 2018/4/12 0012
+  Time: 15:19
+  To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<html>
+<head>
+    <title>无框架的权限管理界面</title>
+    <script type="text/javascript" src="http://lib.sinaapp.com/js/jquery/2.0.3/jquery-2.0.3.min.js"></script>
+</head>
+<body>
+    <jsp:include page="authority.jsp"></jsp:include>
+    <script>
+        var home_base = "${base}";
+        $(function () {
+            myInit();
+        })
+    </script>
+</body>
+</html>

+ 1 - 1
web/WEB-INF/jsp/user/list.jsp

@@ -9,7 +9,7 @@
 <html>
 <head>
     <title>用户列表</title>
-    <script src="http://lib.sinaapp.com/js/jquery/2.0.3/jquery-2.0.3.min.js"></script>
+    <script src="${base}/rs/js/jquery-1.8.3.min.js"></script>
 </head>
 <body>
     <div>

+ 1 - 1
web/WEB-INF/jsp/user/login.jsp

@@ -3,7 +3,7 @@
 <html>
   <head>
     <title>Nutz Demo</title>
-    <script src="${base}/js/jquery-1.8.3.min.js"></script>
+    <script src="${base}/rs/js/jquery-1.8.3.min.js"></script>
   </head>
   <body>
 

+ 1 - 1
web/WEB-INF/jsp/user/profile.jsp

@@ -10,7 +10,7 @@
 <html>
 <head>
     <title>用户详情页</title>
-    <script type="text/javascript" src="http://cdn.staticfile.org/jquery/1.8.3/jquery.min.js"></script>
+    <script type="text/javascript" src="${base}/rs/js/jquery-1.8.3.min.js"></script>
 </head>
 <body>
 

+ 1 - 1
web/index.jsp

@@ -3,7 +3,7 @@
 <html>
   <head>
     <title>Nutz Demo</title>
-    <script src="js/jquery-1.8.3.min.js"></script>
+    <script src="${base}/rs/js/jquery-1.8.3.min.js"></script>
   </head>
   <body>
 

web/js/jquery-1.8.3.min.js → web/rs/js/jquery-1.8.3.min.js