Shiro通過數(shù)據(jù)庫配置后臺管理項目權(quán)限

前言

SpringBoot整合Shiro配置后臺管理項目權(quán)限,普遍的方式是通過添加shiro標(biāo)簽在html文件里面,然后判斷當(dāng)前用戶是否擁有當(dāng)前權(quán)限,來確認(rèn)是否展示當(dāng)前菜單,shiro標(biāo)簽類似如下:

<!--驗證當(dāng)前用戶是否擁有指定權(quán)限。  -->
<a shiro:hasPermission="user:add" href="#" >add用戶</a><!-- 擁有權(quán)限 -->
<!--與hasPermission標(biāo)簽邏輯相反,當(dāng)前用戶沒有制定權(quán)限時,驗證通過。-->
<p shiro:lacksPermission="user:del"> 沒有權(quán)限 </p>
<!--驗證當(dāng)前用戶是否擁有以下所有權(quán)限。-->
<p shiro:hasAllPermissions="user:view, user:add"> 權(quán)限與判斷 </p>

這種方式缺點是需要手寫所有菜單標(biāo)簽,也要加上對應(yīng)的權(quán)限標(biāo)識,在菜單列表比較多的時候,index頁面會顯得很臃腫,而且自己也容易混淆其中的權(quán)限標(biāo)識。上次看了同事管理權(quán)限的方式,感覺很精妙,他是通過在數(shù)據(jù)庫里面創(chuàng)建菜單表,關(guān)聯(lián)權(quán)限表用戶表,然后在進(jìn)入index頁面的時候,通過freemarker模板引擎,循環(huán)創(chuàng)建菜單表,這樣一來,進(jìn)入index頁面之后,只會顯示當(dāng)前用戶擁有權(quán)限的菜單。而且這種方式,在搭建好了后臺配置之后,只需要手動的在數(shù)據(jù)庫里面寫入菜單名,index頁面就會直接顯示,這樣不容易搞混淆。

后臺搭建

數(shù)據(jù)庫表

1.user表

2.user_role表

3.role表

4.permission表

5.role_permission表

6.menu表

代碼

1.Controller

    @GetMapping("/index")
    public String login(Model model) {
        //獲取當(dāng)前用戶名得到菜單
        Subject subject = SecurityUtils.getSubject();
        if(!subject.isAuthenticated()) {
            return "/login";
        }
        //根據(jù)當(dāng)前登錄賬號來獲取當(dāng)前當(dāng)前賬號所擁有權(quán)限的菜單列表
        String username = subject.getPrincipal().toString();
        List<Menus> menuTree = menuService.findMenuTreeByUsername(username);
        model.addAttribute("menuTree",menuTree);
        return "index";
    }

2.Service

public interface MenuService extends IService<Menu> {
    List<Menus> findMenuTreeByUsername(String username);
}

3.ServiceImpl

@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
    @Autowired
    private MenuMapper menuMapper;

    @Override
    public List<Menus> findMenuTreeByUsername(String username) {
        return menuMapper.findMenuTreeByUsername(username);
    }
}

4.Mapper

public interface MenuMapper extends BaseMapper<Menu> {
    /***
     * 根據(jù)用戶名獲取菜單樹
     * @param username
     * @return
     */
    List<Menus> findMenuTreeByUsername(@Param("username")String username);
}

5.Xml

    <resultMap type="com.appointment.model.vo.Menus" id="MenuMap">
        <id property="menuId" column="id" jdbcType="BIGINT" />
        <result property="menuName" column="menu_name" jdbcType="VARCHAR"/>
        <result property="menuIcon" column="menu_icon" jdbcType="VARCHAR"/>
        <collection property="subMenus" ofType="com.appointment.model.vo.Menus">
            <id property="menuId" column="mid" jdbcType="BIGINT" />
            <result property="menuName" column="mname" jdbcType="VARCHAR"/>
            <result property="menuUrl" column="murl" jdbcType="VARCHAR"/>
        </collection>
    </resultMap>

    <select id="findMenuTreeByUsername" resultMap="MenuMap">
        select pm.id,pm.menu_name,pm.menu_icon,m.id mid,m.menu_name mname,m.menu_url murl
        from user u
        inner join user_role ur on u.id=ur.user_id
        inner join role role on ur.role_id=role.id
        inner join role_permission rp on role.id=rp.role_id
        inner join permission p on rp.permission_id=p.id
        inner join menu m on p.menu_id=m.id
        inner join menu pm on m.menu_id=pm.id
        where u.username=#{username}
    </select>

6.VO對象

@Data
public class Menus {
    private Long menuId;
    private String menuName;
    private String menuUrl;
    private String menuIcon;
    private List<Menus> subMenus;
}
前端頁面生成菜單列表
//此處是通過freemarker模板引擎生成,用thymeleaf的話也是可以的
                <ul class="layui-nav layui-nav-tree" lay-shrink="all" id="LAY-system-side-menu" lay-filter="layadmin-system-side-menu">
                menuTree
                <#list menuTree as menus>
                <li class="layui-nav-item">
                    <a href="javascript:;"><i class="${menus.menuIcon}"></i>
                        ${menus.menuName}
                    </a>
                    <dl  class="layui-nav-child">
                        <#list menus.subMenus as s>
                        <dd data-name="${s.menuName}" >
                            <a lay-href="${request.contextPath}${s.menuUrl}">${s.menuName}</a>
                        </dd>
                        </#list><
                    /dl>
                </li>
                </#list>
            </ul>
Shiro配置

1.ShiroConfig添加權(quán)限標(biāo)識
ShiroConfig文件里面,通過查詢在數(shù)據(jù)庫里配置的全選標(biāo)識,給所有的需要權(quán)限的資源添加權(quán)限標(biāo)識,同時,通過這種方式,直接省略了在每個接口上面添加@RequiresPermissions("user:add")類似的注解。

    /**
     * 定義shiroFilter過濾器并注入securityManager
     * @param manager
     * @return
     */
    @Bean("shiroFilter")       //必須叫這個。
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //設(shè)置securityManager
        bean.setSecurityManager(manager);
        //設(shè)置登錄頁面
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/auth.html");
        //定義過濾器
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //配置記住我或認(rèn)證通過可以訪問的地址
        filterChainDefinitionMap.put("/index", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources", "anon");
        filterChainDefinitionMap.put("/swagger-resources/configuration/security", "anon");
        filterChainDefinitionMap.put("/swagger-resources/configuration/ui", "anon");
        filterChainDefinitionMap.put("/api/**", "anon");
        //通過查詢在數(shù)據(jù)庫里面的權(quán)限標(biāo)識,循環(huán)給自己設(shè)定的字段添加標(biāo)識權(quán)限
        List<Permission> list = permissionMapper.getAll();
        for (Permission permission : list) {
            filterChainDefinitionMap.put(permission.getResource(), "perms["+permission.getSn()+"]");
        }
        //需要登錄訪問的資源 , 一般將/**放在最下邊
        filterChainDefinitionMap.put("/**", "anon");   //    , 不需要認(rèn)證。
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

permissionMapper

public interface PermissionMapper extends BaseMapper<Permission> {
    List<Permission> getPermissionsByUserName(@Param("username")String username);

    List<Permission> getAll();
}

Xml

    <select id="getAll" resultMap="BaseResultMap">
        select * from permission
    </select>

2.AuthRealm授權(quán)

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("Shiro開始授權(quán)操作");
        String username = SecurityUtils.getSubject().getPrincipal().toString();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set<String> roles=new HashSet<>();
        //得到一個用戶的所有角色
        List<Role> rolesList = roleMapper.getRolesByUserName(username);
        for (Role role : rolesList) {
            roles.add(role.getRoleName());
        }
        //得到一個用戶的所有權(quán)限,給當(dāng)前用戶授權(quán)
        List<Permission> permissionsList = permissionMapper.getPermissionsByUserName(username);
        for (Permission permission : permissionsList) {
            authorizationInfo.addStringPermission(permission.getSn());
        }
        authorizationInfo.setRoles(roles);
        return authorizationInfo;
    }

Mapper

public interface RoleMapper extends BaseMapper<Role> {
    List<Role> getRolesByUserName(@Param("username")String username);
}

Xml

    <select id="getRolesByUserName" resultMap="BaseResultMap">
        select
        r.id,
        r.role_name,
        r.remake
        from role r
        left join user_role ur on ur.role_id=r.id
        left join user u on u.id=ur.user_id
        where u.username=#{username}
    </select>

Xml

    <resultMap id="BaseResultMap" type="com.appointment.model.Permission">
        <id column="id" property="id" />
        <result column="permission_name" property="permissionName" />
        <result column="sn" property="sn" />
        <result column="resource" property="resource" />
        <result column="menu_id" property="menuId" />
    </resultMap>
    <select id="getPermissionsByUserName" resultMap="BaseResultMap">
        select
        p.id id,
        p.permission_name,
        p.resource resource,
        p.sn sn,
        p.menu_id
        from permission p
        left join role_permission rp on rp.permission_id=p.id
        left join role r on r.id=rp.role_id
        left join user_role ur on ur.role_id=r.id
        left join user u on u.id=ur.user_id
        where u.username=#{username}
    </select>

總結(jié)

參考表數(shù)據(jù)

menu

permission表

role_permission

role

role_user


user


前端頁面效果


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容