基于URL的权限验证流程总结

之前写过一篇博客:拦截器实现基于Url的权限管理,文章中讲解了怎么用拦截器实现url权限认证,这仅仅是权限管理的一部分。今天这篇博客就来说说一个项目完整的权限认证流程。

1、 准备权限数据

服务器启动时,通过InitListener监听器,将数据库中的权限信息查询出来,并放到ServletContext中。其中权限信息分两类:顶层菜单(系统管理)和其他权限信息url。ServletContext中放一些共享数据。

public class InitListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
        // 获取容器与相关的Service对象
        ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
        PrivilegeService privilegeService = (PrivilegeService) ac.getBean("privilegeServiceImpl");

        // 准备数据:topPrivilegeList
        List<Privilege> topPrivilegeList = privilegeService.findTopList();
        sce.getServletContext().setAttribute("topPrivilegeList", topPrivilegeList);
        System.out.println("------------> 已准备数据 <------------");

            // 准备数据:allPrivilegeUrls
            Collection<String> allPrivilegeUrls = privilegeService.getAllPrivilegeUrls();
            sce.getServletContext().setAttribute("allPrivilegeUrls", allPrivilegeUrls);
            System.out.println("------------> 已准备数据allPrivilegeUrls <------------");
    }

    public void contextDestroyed(ServletContextEvent arg0) {

    }
}

2、编写拦截器CheckPrivilegeInterceptor,拦截用户的所有请求

public class CheckPrivilegeInterceptor extends AbstractInterceptor {

    public String intercept(ActionInvocation invocation) throws Exception {

    // 获取信息
    User user = (User) ActionContext.getContext().getSession().get("user"); // 当前登录用户
    String namespace = invocation.getProxy().getNamespace();
    String actionName = invocation.getProxy().getActionName();
    String privUrl = namespace + actionName; // 对应的权限URL

    // 如果未登录
    if (user == null) {
        if (privUrl.startsWith("/user_login")) { // "/user_loginUI", "/user_login"
            // 如果是去登录,就放行
            return invocation.invoke();
        } else {
            // 如果不是去登录,就转到登录页面
            return "loginUI";
        }
    }
    // 如果已登 录,就判断权限
    else {
        if (user.hasPrivilegeByUrl(privUrl)) {
            // 如果有权限,就放行
            return invocation.invoke();
        } else {
            // 如果没有权限,就转到提示页面
            return "noPrivilegeError";
        }
    }
    }

}

3、其中User实体提供两个权限判断方法

hasPrivilegeByName( ):判断本用户是否有指定名称的权限,主要查询用户拥有哪个菜单权限,从而显示到页面

hasPrivilegeByUrl( ):判断本用户是否有指定URL的权限,主要是界面的增删改查权限url

    /**
     * 判断本用户是否有指定名称的权限
     * 主要查询用户拥有哪个菜单权限,从而显示到页面
     * @param name,菜单名称
     * @return
     */
    public boolean hasPrivilegeByName(String name)
    {
        // 超级管理有所有的权限
        if (isAdmin())
        {
            return true;
        }

        // 普通用户要判断是否含有这个权限
        for (Role role : roles)
        {
            for (Privilege priv : role.getPrivileges())
            {
                if (priv.getName().equals(name))
                {
                    return true;
                }
            }
        }
        return false;
    }

------------------------华丽的分割线-------------------------

    /*
     * 判断本用户是否有指定URL的权限
     * 
     * @param privUrl  主要是界面的增删改查权限url
     * @return
     */
    public boolean hasPrivilegeByUrl(String privUrl) {
        // 超级管理有所有的权限
        if (isAdmin()) {
            return true;
        }

        // >> 去掉后面的参数
        int pos = privUrl.indexOf("?");
        if (pos > -1) {
            privUrl = privUrl.substring(0, pos);
        }
        // >> 去掉UI后缀
        if (privUrl.endsWith("UI")) {
            privUrl = privUrl.substring(0, privUrl.length() - 2);
        }

        // 如果本URL不需要控制,则登录用户就可以使用
        //allPrivilegeUrls是在监听器中添加的
        Collection<String> allPrivilegeUrls = (Collection<String>) ActionContext.getContext().getApplication().get("allPrivilegeUrls");
        if (!allPrivilegeUrls.contains(privUrl)) {
            return true;
        } else {
            // 普通用户要判断是否含有这个权限
            for (Role role : roles) {
                for (Privilege priv : role.getPrivileges()) {
                    if (privUrl.equals(priv.getUrl())) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

4、 用户进行登录操作

首先进行登录校验,不成功给出提示;成功之后,将用户的信息放入到session中

    /** 登录 */
    public String login() throws Exception
    {
        User user = userService.findByLoginNameAndPassword(
                model.getLoginName(), model.getPassword());
        if (user == null)
        {
            addFieldError("login", "用户名或密码不正确!");
            return "loginUI";
        }
        else
        {
            // 登录用户,将登陆用户放入session
            ActionContext.getContext().getSession().put("user", user);
            return "toIndex";
        }
    }

5、用户登录之后显示首页的菜单信息

页面显示菜单信息,需要调用session.user.hasPrivilegeByName(name)方法判断是否有权限显示,其中topPrivilegeList是在初始化监听器时放入的顶级菜单信息。

<ul id="MenuUl">

    <%-- 显示一级菜单,在初始化时用Listener添加到ServletContextEvent中 --%>
    <s:iterator value="#application.topPrivilegeList">
        <s:if test="#session.user.hasPrivilegeByName(name)">
        <li class="level1">
            <div onClick="menuClick(this);" class="level1Style">
                <img src="style/images/MenuIcon/${id}.gif" class="Icon" />
                ${name}
            </div>
            <ul style="" class="MenuLevel2" id="aa">
                <%-- 显示二级菜单 --%>
                <s:iterator value="children">
                    <s:if test="#session.user.hasPrivilegeByName(name)">
                    <li class="level2">
                        <div class="level2Style">
                            <img src="style/images/MenuIcon/menu_arrow_single.gif" />
                            <a target="right" href="${pageContext.request.contextPath}${url}.action"> ${name}</a>
                        </div>
                    </li>
                    </s:if>
                </s:iterator>
            </ul> 
        </li>
        </s:if>
    </s:iterator>

</ul>

6、用户点击某个页面

用户点击某个页面,权限判断需要有两步,一是页面显示权限,而是url访问权限。

①修改Struts2的页面显示类AnchorTag ,添加判断:如果没有权限的话,就不显示该标签,让用户根本看不到。比如添加按钮等。

    /*
     * $Id: AnchorTag.java 768855 2009-04-27 02:09:35Z wesw $
     *
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * "License"); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *  http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */

    package org.apache.struts2.views.jsp.ui;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.jsp.JspException;

    import org.apache.struts2.components.Anchor;
    import org.apache.struts2.components.Component;

    import cn.itcast.oa.domain.User;

    import com.opensymphony.xwork2.util.ValueStack;

    /**
     * @see Anchor
     */
    public class AnchorTag extends AbstractClosingTag {

            private static final long serialVersionUID = -1034616578492431113L;

            protected String href;
            protected String includeParams;
            protected String scheme;
            protected String action;
            protected String namespace;
            protected String method;
            protected String encode;
            protected String includeContext;
            protected String escapeAmp;
            protected String portletMode;
            protected String windowState;
            protected String portletUrlType;
            protected String anchor;
            protected String forceAddSchemeHostAndPort;

            @Override
            public int doEndTag() throws JspException {
                    // 当前登录用户
                    User user = (User) pageContext.getSession().getAttribute("user");

            // 当前准备显示的链接对应的权限URL
            // >> 在开头加上'/'
            String privUrl = "/" + action;

            if (user.hasPrivilegeByUrl(privUrl)) {
                    return super.doEndTag(); // 正常的生成并显示超链接 标签,并继续执行页面中后面的代码
            } else {
                    return EVAL_PAGE; // 不生成与显示超链接 标签,只是继续执行页面中后面的代码
            }
            }

            public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
                    return new Anchor(stack, req, res);
            }

            protected void populateParams() {
                    super.populateParams();

            Anchor tag = (Anchor) component;
            tag.setHref(href);
            tag.setIncludeParams(includeParams);
            tag.setScheme(scheme);
            tag.setValue(value);
            tag.setMethod(method);
            tag.setNamespace(namespace);
            tag.setAction(action);
            tag.setPortletMode(portletMode);
            tag.setPortletUrlType(portletUrlType);
            tag.setWindowState(windowState);
            tag.setAnchor(anchor);

            if (encode != null) {
                    tag.setEncode(Boolean.valueOf(encode).booleanValue());
            }
            if (includeContext != null) {
                    tag.setIncludeContext(Boolean.valueOf(includeContext).booleanValue());
            }
            if (escapeAmp != null) {
                    tag.setEscapeAmp(Boolean.valueOf(escapeAmp).booleanValue());
            }
            if (forceAddSchemeHostAndPort != null) {
                    tag.setForceAddSchemeHostAndPort(Boolean.valueOf(forceAddSchemeHostAndPort).booleanValue());
            }
            }

            public void setHref(String href) {
                    this.href = href;
            }

            public void setEncode(String encode) {
                    this.encode = encode;
            }

            public void setIncludeContext(String includeContext) {
                    this.includeContext = includeContext;
            }

            public void setEscapeAmp(String escapeAmp) {
                    this.escapeAmp = escapeAmp;
            }

            public void setIncludeParams(String name) {
                    includeParams = name;
            }

            public void setAction(String action) {
                    this.action = action;
            }

            public void setNamespace(String namespace) {
                    this.namespace = namespace;
            }

            public void setMethod(String method) {
                    this.method = method;
            }

            public void setScheme(String scheme) {
                    this.scheme = scheme;
            }

            public void setValue(String value) {
                    this.value = value;
            }

            public void setPortletMode(String portletMode) {
                    this.portletMode = portletMode;
            }

            public void setPortletUrlType(String portletUrlType) {
                    this.portletUrlType = portletUrlType;
            }

            public void setWindowState(String windowState) {
                    this.windowState = windowState;
            }

            public void setAnchor(String anchor) {
                    this.anchor = anchor;
            }

            public void setForceAddSchemeHostAndPort(String forceAddSchemeHostAndPort) {
                    this.forceAddSchemeHostAndPort = forceAddSchemeHostAndPort;
            }
    }

② 拦截用户输入的url,用户可能直接在浏览器栏输入添加的url,这时候也要拦截并判断是否有权限访问,没有的话跳转到无权访问界面。

判断方法就在第二步的CheckPrivilegeInterceptor 拦截器中

    // 如果未登录
    if (user == null) {
            if (privUrl.startsWith("/user_login")) { // "/user_loginUI", "/user_login"
                    // 如果是去登录,就放行
                    return invocation.invoke();
            } else {
                    // 如果不是去登录,就转到登录页面
                    return "loginUI";
            }
    }
    // 如果已登 录,就判断权限
    else {
            if (user.hasPrivilegeByUrl(privUrl)) {
                    // 如果有权限,就放行
                    return invocation.invoke();
            } else {
                    // 如果没有权限,就转到提示页面
                    return "noPrivilegeError";
            }
    }

小结

基于URL的权限验证的整个流程大概也就这样了,梳理完感觉清晰很多。不过这种基于URL拦截的方式实现权限认证也太繁琐了,在上面也看到了,需要咱们完成很多工作,不利于项目的维护。不过后面会讲解一种高大上,并且快速简单的权限开发方式Shiro框架,拭目以待。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值