权限管理框架实现(1)--Struts切面处理

要做一款权限架构,就要适用几个流行的相关框加,struts2是我们公司首先需要考虑的,考虑到侵入性,决定通过切面的方式,在每个Action前进行权限验证,基本思路是:

        1,自定义通用权限注解

        2,开发抽象切面,预留传入uid的接口

        3,配置struts切面,做权限拦截


以下源码是对上边功能的实现:

1,权限注解

/**
 * 自定义权限注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authority {

    /**
     * 权限码
     * @return
     */
    String authorityCode();

}

2,sturts抽象切面

public abstract class ELInterceptor implements Interceptor {

    private IVerificationUser verificationUser;
    private ILogicUserAreaFranchiseeService logicUserAreaFranchiseeService;
    private Result result = new Result(true);

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    protected final Log log = LogFactory.getLog(this.getClass());

    @Override
    public void init() {

    }

    /**
     * 拦截类并作权限验证
     * @param invocation
     * @return
     * @throws Exception
     */
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        log.info("=intercept=>Authority Intercept");
        // TODO Auto-generated method stub
        String methodName = invocation.getProxy().getMethod();
        Method currentMethod = invocation.getAction().getClass().getMethod(methodName);
        Method[] methods = invocation.getAction().getClass().getMethods();
        initAuthCode(methods);

        String isTest = (String) ServletActionContext.getRequest().getParameter("authistest");

        //如果该方法请求是需要进行验证的时候执行以下逻辑
        if (currentMethod.isAnnotationPresent(Authority.class)) {
            //取得权限验证的注解
            Authority authority = currentMethod.getAnnotation(Authority.class);

            log.info("=intercept=> get authorityCode");
            //取得当前请求的注解的authorityCode
            String authorityCode = authority.authorityCode();

             /* *
             * 然后可以在此判断当前用户是否拥有对应的权限,如果没有可以跳到指定的无权限提示页面,如果拥有则可以
             * 继续往下执行。
             **/
            boolean ispass =false;
            ispass = Boolean.parseBoolean(getFromVm(authorityCode));
            if (ispass){
                if(isTest==null ||isTest.trim().isEmpty()){
                    return invocation.invoke();
                }else{
                    return "hasauth";
                }
            } else {
                log.info("=intercept=> user not have  this authorityCode");
                writeJson("<html auth='NOAUTH'></html>");
                return "noauth";
            }
        }
        log.info("<=intercept=>Authority Intercept");
        if(isTest==null ||isTest.trim().isEmpty()){
            return invocation.invoke();
        }else{
            return "hasauth";
        }
    }

    private Boolean checkOnline(AuthCheckDomain authCheckDomain){
        boolean isPass = false;
        AuthorityUser authorityUser =null;
        PublicResult<Boolean> publicResult =verificationUser.hasAuth(authCheckDomain);
        if(publicResult!=null&&publicResult.isSuccess()){
            isPass = publicResult.getResult();
            if(publicResult.getAuthorityUser()!=null ){
                authorityUser=publicResult.getAuthorityUser();
                authorityUser.setCreated(new Date());
                AuthCache.authMap.put(authCheckDomain.getAuthCode().trim(),authorityUser);
            }
        }
        return isPass;
    }

    private void writeJson(String json){
        try {
            HttpServletResponse response = ServletActionContext.getResponse();
            response.setHeader("contentType", "application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setDateHeader("Expires", 0L);
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            ServletOutputStream out = response.getOutputStream();
            out.write(json.getBytes("UTF-8"));
            out.flush();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void initAuthCode(Method[] methods){
        for(Method currentMethod :methods){
            //如果该方法请求是需要进行验证的时候执行以下逻辑
            if (currentMethod.isAnnotationPresent(Authority.class)) {
                //取得权限验证的注解
                Authority authority = currentMethod.getAnnotation(Authority.class);

                log.info("=intercept=> get authorityCode");
                //取得当前请求的注解的authorityCode
                String authorityCode = authority.authorityCode();
                isAuthPass(authorityCode);
            }
        }

        toVm(result);
    }

    private boolean  isAuthPass(String authorityCode){
        log.info("=isAuthPass=>");
        AuthCheckDomain authCheckDomain = new AuthCheckDomain();
        Long uid=getUid();
        String ip=getIP();
        UserAreaFranchisee userArea =  getUserArea(uid);
        Long provinceId=null;
        Long cityId=null;
        Long townId=null;
        if(userArea!=null){
            provinceId=Long.valueOf(userArea.getProvinceid());
            cityId=Long.valueOf(userArea.getCityid());
            townId=Long.valueOf(userArea.getCountyid());
        }

        authCheckDomain.setAuthCode(authorityCode);
        authCheckDomain.setUid(uid);
        authCheckDomain.setIp(ip);
        authCheckDomain.setProvinceId(provinceId);
        authCheckDomain.setCityId(cityId);
        authCheckDomain.setTownId(townId);

        boolean ispass = false;
        AuthorityUser authorityUser =null;
        if(AuthCache.authMap!=null && AuthCache.authMap.containsKey(authorityCode) && AuthCache.authMap.get(authorityCode)!=null ){
            authorityUser = AuthCache.authMap.get(authorityCode);
            int nowLong = DateUtil.getCurrentTime();
            long checkTime = nowLong-(authorityUser.getCreated().getTime()/1000);
            if(checkTime> RedisIntValueUtils.MapExpireSeconds.getIntValue()){
                ispass = checkOnline(authCheckDomain);
            }
            ispass=CheckAuth.hasAuth(authorityUser,authCheckDomain);
        }else{
            ispass=checkOnline(authCheckDomain);
        }

        toVmAuthPass(authorityCode, ispass);
        return ispass;
    }

    private void toVmAuthPass(String authCode,boolean isPass){
        result.addDefaultModel(authCode,isPass);
    }


    private void toVm(Result result){
        ValueStack context = ActionContext.getContext().getValueStack();
        Set set = result.keySet();
        Iterator resultCode = set.iterator();
        context.set("textProvider", this);
        context.set("datePickerLocale", this.getDatePickerLocale());

        String text;
        while(resultCode.hasNext()) {
            text = (String)resultCode.next();
            context.set(text, result.get(text));
        }
    }

    private String getFromVm(String authCode){
        ValueStack context = ActionContext.getContext().getValueStack();
        Map map = (Map)context.peek();
        String result = ((Boolean)map.get(authCode)).toString();
        return result;
    }

    private String getDatePickerLocale() {
        String locale = this.getLocale().toString().toLowerCase();
        String[] arr = locale.split("_");
        if(arr[0].equals("en")) {
            locale = arr[0];
        } else {
            locale = arr[0] + "-" + arr[1];
        }

        return locale;
    }

    public Locale getLocale() {
        ActionContext ctx = ActionContext.getContext();
        if(ctx != null) {
            return ctx.getLocale();
        } else {
            return null;
        }
    }

    private UserAreaFranchisee getUserArea(Long uid){
        UserAreaFranchisee userAreaFranchisee = null;
        String userJson = RedisUtils.get(RedisKeyUtils.Redis_Key_UserArea.getKeyStr()+uid,String.class);
        if(userJson==null || userJson.trim().isEmpty()){
            userAreaFranchisee =  getUserAreaByDubbo(uid);
            try {
                if (userAreaFranchisee != null) {
                    RedisUtils.set(RedisKeyUtils.Redis_Key_UserArea.getKeyStr() + uid, JSON.toJSONString(userAreaFranchisee), RedisIntValueUtils.Redis_ExpireSeconds_UserArea.getIntValue());
                }
            }catch (Exception e){
                log.error("=getUserArea=>set reids error",e);
            }
        }else{
            try {
                userAreaFranchisee = JSON.parseObject(userJson, UserAreaFranchisee.class);
            }catch(Exception e){
                log.error("=getUserArea=> json error",e);
                userAreaFranchisee=null;
            }
        }

        return userAreaFranchisee;

    }

    private UserAreaFranchisee getUserAreaByDubbo(Long uid){
        com.el.common.result.PublicResult<UserAreaFranchisee> result =null;
        try {
            result = logicUserAreaFranchiseeService.getUserAreaFranchisee(uid.intValue());
        }catch (Exception e){
            log.error("==>error" ,e);
            result=null;
        }
        if(result==null || !result.isSuccess() || result.getResult()==null){
            return null;
        }else{
            return result.getResult();
        }
    }

    private Long getProvinceId(Long uid) {
        return null;
    }

    public Long getCityId(Long uid) {
        return null;
    }


    public Long getTownId(Long uid) {
        return null;
    }



    public abstract Long getUid();
    public abstract String getIP();

    public IVerificationUser getVerificationUser() {
        return verificationUser;
    }

    public void setVerificationUser(IVerificationUser verificationUser) {
        this.verificationUser = verificationUser;
    }

    public ILogicUserAreaFranchiseeService getLogicUserAreaFranchiseeService() {
        return logicUserAreaFranchiseeService;
    }

    public void setLogicUserAreaFranchiseeService(ILogicUserAreaFranchiseeService logicUserAreaFranchiseeService) {
        this.logicUserAreaFranchiseeService = logicUserAreaFranchiseeService;
    }
}


此处预留了两个抽象方法,依赖具体客户端实现,分别是获取用户id和获取ip

    public abstract Long getUid();
    public abstract String getIP();


具体切面(抽象类实现)

@Service
public  class ELInterceptorImpl extends  ELInterceptor {

    @Autowired
    private IVerificationUser verificationUser;

    @Autowired
    private ILogicUserAreaFranchiseeService logicUserAreaFranchiseeService;

    public  Long getUid(){
        String suid = (String) ServletActionContext.getRequest().getSession().getAttribute("uid");
        Long uid=Long.valueOf((suid==null || suid.isEmpty())?"0":suid);
        return uid;
    }

    @Override
    public String getIP() {
        //这里可以从session里面取得当前的用户
        String suip = (String) ServletActionContext.getRequest().getSession().getAttribute("ip");
        return suip;

    }
}

注意:struts切面抽象类中,两个具体的业务服务不能使用spring自动注入,因为spring是扫描包下的类,有注解的维护一个单例的存在,而抽象类不能实例化,所以会报错,我们可以在具体实现类中,注入两个属性,结果是一样的。

3,struts配置

<interceptors>
            <interceptor name="authIntercept" class="com.el.authority.service.intercept.ELInterceptorImpl"/>
            <interceptor-stack name="strutsDefaultStack">

                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload">
                    <param name="maximumSize">4194304</param>
                    <!--单个文件最大4M -->
                    <param name="allowedTypes">image/x-png,image/png,image/gif,image/jpeg,image/jpg,image/pjpeg,text/plain,application/octet-stream</param>
                    <param name="allowedExtensions">jpg,jpeg,png,gif,txt,vm</param>
                </interceptor-ref>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">dojo\..*,^struts\..*,.*\\u0023.*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="authIntercept"/>
            </interceptor-stack>
            <interceptor-stack name="taskInterceptor">
                <interceptor-ref name="strutsDefaultStack"/>
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="taskInterceptor"/>
        <global-results>
            <result name="exception">/WEB-INF/vm/error.vm</result>
            <result name="error">/WEB-INF/vm/error.vm</result>
            <result name="input">/WEB-INF/vm/convertError.vm</result>
            <result name="noauth">/WEB-INF/vm/noauthority.vm</result>
            <result name="hasauth">/WEB-INF/vm/hasauth.vm</result>
        </global-results>


4,struts图示

        下图是struts架构的实现,对应到刚刚的配置文件中,我们就能很容易分辨出切面的切入位置



总结:

        struts切面的实现,网上有很多例子,大家不防多多参考,本文中的例子主要是给大家交代清楚整体结构,通过这些后台代码及配置,我们就能全局对用户权限控制,进行控制,但是还是有些细节,篇幅所限,大家参考后续的文章,其中会介绍ValueStack的使用,前台控件的自动显隐等。






转载于:https://my.oschina.net/u/3627638/blog/1489211

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值