使用shiro在網關層解決過濾url


      最近公司的服務有這么一個需求:有兩套后台服務,在請求后台的的時候,會先由網關層進行權限攔截。根據當前登錄用戶擁有的菜單、角色以及請求的url地址進行攔截能不能請求到另外的服務地址。網關層使用的shiro的權限管理,已經封裝好了角色、權限和賬號。但是在攔截uri的問題上一直沒想明白。然后就看shiro的源碼,從我們現有的登錄看起。最終總算找到了一種解決方案

      shiro在登錄時候,使用的是Subject的login方法。

  

Subject currentLoginUser = SecurityUtils.getSubject();
		//A:是否已經登錄
		if(currentLoginUser.isAuthenticated()) {
			Boolean isAjax = (Boolean) request.getAttribute("X_IS_AJAX");
			if(  isAjax  ) {
				return AjaxResponse.success( null );
			}else {
				response.sendRedirect(homepageUrl);
				return null;
			}
		}
		//B:查詢用戶信息
		CarAdmUser user = carAdmUserExMapper.queryByAccount(username,null);
		if(user==null){
			return AjaxResponse.fail(RestErrorCode.USER_NOT_EXIST) ;
		}
		//C:密碼不正確
		String enc_pwd = PasswordUtil.md5(password, user.getAccount());
		if(!enc_pwd.equalsIgnoreCase(user.getPassword())) {
			return AjaxResponse.fail(RestErrorCode.USER_PASSWORD_WRONG) ;
		}
		
		//E: 用戶狀態
		if(user.getStatus()!=null && user.getStatus().intValue()==100 ){
			return AjaxResponse.fail(RestErrorCode.USER_INVALID) ;
		}
		//F: 執行登錄
		try {
			//shiro登錄
			UsernamePasswordToken token = new UsernamePasswordToken( username, password.toCharArray() );
			currentLoginUser.login(token);
			//記錄登錄用戶的所有會話ID,以支持“系統管理”功能中的自動會話清理
			String sessionId =  (String)currentLoginUser.getSession().getId() ;
			redisSessionDAO.saveSessionIdOfLoginUser(username, sessionId);

			redisTemplate.delete(redis_login_key);
			redisTemplate.delete(redis_getmsgcode_key);

		}catch(AuthenticationException aex) {
			return AjaxResponse.fail(RestErrorCode.USER_LOGIN_FAILED) ;
		}
		//返回登錄成功
		Boolean isAjax = (Boolean) request.getAttribute("X_IS_AJAX");
		if(  isAjax  ) {
			return AjaxResponse.success( null );
		}else {
			response.sendRedirect(homepageUrl);
			return null;
		}

  

currentLoginUser.login(token);是重寫了shiro里面的AuthorizingRealm()
package com.sq.transportmanage.gateway.service.shiro.realm;

import com.sq.transportmanage.gateway.dao.entity.driverspark.CarAdmUser;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasPermissionExMapper;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasRoleExMapper;
import com.sq.transportmanage.gateway.service.auth.MyDataSourceService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**認證  與  權限  **/

/**
 * 這個就是shiro SSOLogin 的用戶獲取的屬性配置
 */
@Component
public class UsernamePasswordRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(UsernamePasswordRealm.class);

	@Autowired
	private MyDataSourceService myDataSourceService;

	@Autowired
	private SaasPermissionExMapper saasPermissionExMapper;

	@Autowired
	private SaasRoleExMapper saasRoleExMapper;
	
    /**重寫:獲取用戶的身份認證信息**/
	@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
		logger.info( "[獲取用戶的身份認證信息開始]authenticationToken="+authenticationToken);
		try {
			UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
			CarAdmUser adMUser = myDataSourceService.queryByAccount(token.getUsername());
			SSOLoginUser loginUser = new SSOLoginUser();  //當前登錄的用戶
			loginUser.setId( adMUser.getUserId() );                //用戶ID
			loginUser.setLoginName( adMUser.getAccount() );//登錄名
			loginUser.setMobile( adMUser.getPhone() );         //手機號碼
			loginUser.setName( adMUser.getUserName() );    //真實姓名
			loginUser.setEmail(adMUser.getEmail()); //郵箱地址
			loginUser.setType( null );   //
			loginUser.setStatus( adMUser.getStatus() );           //狀態
			loginUser.setAccountType( adMUser.getAccountType() );   //自有的帳號類型:[100 普通用戶]、[900 管理員]
			loginUser.setLevel(adMUser.getLevel());
			loginUser.setUuid(adMUser.getUuid());

			List<String> menuUrlList = saasPermissionExMapper.queryPermissionCodesOfUser(adMUser.getUserId() );
			loginUser.setMenuUrlList(menuUrlList);

			//---------------------------------------------------------------------------------------------------------數據權限BEGIN

			logger.info( "[獲取用戶的身份認證信息]="+loginUser);
			return new SimpleAuthenticationInfo(loginUser, authenticationToken.getCredentials()  ,  this.getName() );
		} catch (Exception e) {
			logger.error("獲取用戶的身份認證信息異常",e);
			return null;
		}
	}


	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		SSOLoginUser loginUser = (SSOLoginUser) principalCollection.getPrimaryPrincipal();
		String account = loginUser.getLoginName(); //登錄名

		List<String> perms_string = saasPermissionExMapper.queryPermissionCodesOfUser(  loginUser.getId() );
		List<String> roles_string   = saasRoleExMapper.queryRoleCodesOfUser( loginUser.getId() );

		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		Set<String> roles = new HashSet<String>( roles_string );
		authorizationInfo.setRoles( roles );
		logger.info( "[獲取用戶授權信息(角色)] "+account+"="+roles);

		Set<String> perms = new HashSet<String>( perms_string );
		authorizationInfo.setStringPermissions(perms);
		logger.info( "[獲取用戶授權信息(權限)] "+account+"="+perms);
		return authorizationInfo;
	}

	@Override
    public Object getAuthorizationCacheKey(PrincipalCollection principals) {
		SSOLoginUser loginUser = (SSOLoginUser) principals.getPrimaryPrincipal();
		String account = loginUser.getLoginName(); //登錄名
        return "-AuthInfo-"+account;
    }
	

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
}

  通過這個可以把登錄信息放入shiro里面,下面是具體的菜單權限。正常來說,使用shiro的注解

@RequiresPermissions(value = {"value"})
就可以正常來使用了。@RequiresPermissions的源碼里面使用的是aop的方式,通過判斷value值是否在某個集合里面來判斷是否有權限(參考:https://blog.csdn.net/xiewenfeng520/article/details/89447749
。但是我們這個項目有些不同,這個是網關層,權限是在網關做好配置,然后進行判斷能否跳轉到指定的url。我白天一直沒想好怎么去攔截這個url,因為我的表關系里面url地址不是一個必傳的參數。然后看了shiro的源碼實現后,
我發現自己的思路沒問題,但是url必傳。 於是方案改成了這樣:

1)寫一個攔截器,攔截url地址
2)登錄后,將用戶含有的菜單權限放到shiro里面
3)在攔截器里面判斷,如果是管理員,直接通過,如果不是,看該用戶是否有該權限。
具體實現代碼:
1) zuul攔截器:
package com.sq.transportmanage.gateway.api.web.filter;

import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.sq.transportmanage.gateway.api.common.AuthEnum;
import com.sq.transportmanage.gateway.service.shiro.realm.SSOLoginUser;
import com.sq.transportmanage.gateway.service.shiro.session.WebSessionUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @program: sq-union-manage
 * @description: AccessFilter
 * @author: zjw
 * @create: 2020-02-23 18:57
 **/
@Component
@RequiresPermissions("/")
public class AccessFilter extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(AccessFilter.class);

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        logger.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
        SSOLoginUser loginUser = WebSessionUtil.getCurrentLoginUser();
        logger.info(String.format("%s loginUser %s", loginUser.getLoginName(), loginUser.getName()));
        /**用戶是否有權限**/
        boolean bl = false;
        //如果是管理員 直接通過
        if(AuthEnum.MANAGE.getAuthId().equals(loginUser.getAccountType())){
            bl = true;
        }else {
            String uri = request.getRequestURI().toString();
            List<String> menuUrl = loginUser.getMenuUrlList();
            if(menuUrl.contains(uri)){
                bl = true;
            }
        }
        if(bl){
            ctx.addZuulRequestHeader("user_token",JSONObject.toJSONString(loginUser));
        }else{
            ctx.setSendZuulResponse(false);// 過濾該請求,不對其進行路由
            ctx.setResponseStatusCode(401);// 返回錯誤碼
            ctx.setResponseBody("{\"code\":0,\"result\":\"網關驗證失敗!驗證方式為2\"}");// 返回錯誤內容
            ctx.set("isSuccess", false);
        }

        //TODO 在此處增加權限判斷即可
        return ctx;
    }

}

  2)shiro存放權限:

 

package com.sq.transportmanage.gateway.service.shiro.realm;

import com.sq.transportmanage.gateway.dao.entity.driverspark.CarAdmUser;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasPermissionExMapper;
import com.sq.transportmanage.gateway.dao.mapper.driverspark.ex.SaasRoleExMapper;
import com.sq.transportmanage.gateway.service.auth.MyDataSourceService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**認證  與  權限  **/

/**
 * 這個就是shiro SSOLogin 的用戶獲取的屬性配置
 */
@Component
public class UsernamePasswordRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(UsernamePasswordRealm.class);

    @Autowired
    private MyDataSourceService myDataSourceService;

    @Autowired
    private SaasPermissionExMapper saasPermissionExMapper;

    @Autowired
    private SaasRoleExMapper saasRoleExMapper;
    
    /**重寫:獲取用戶的身份認證信息**/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
        logger.info( "[獲取用戶的身份認證信息開始]authenticationToken="+authenticationToken);
        try {
            UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
            CarAdmUser adMUser = myDataSourceService.queryByAccount(token.getUsername());
            SSOLoginUser loginUser = new SSOLoginUser();  //當前登錄的用戶
            loginUser.setId( adMUser.getUserId() );                //用戶ID
            loginUser.setLoginName( adMUser.getAccount() );//登錄名
            loginUser.setMobile( adMUser.getPhone() );         //手機號碼
            loginUser.setName( adMUser.getUserName() );    //真實姓名
            loginUser.setEmail(adMUser.getEmail()); //郵箱地址
            loginUser.setType( null );   //
            loginUser.setStatus( adMUser.getStatus() );           //狀態
            loginUser.setAccountType( adMUser.getAccountType() );   //自有的帳號類型:[100 普通用戶]、[900 管理員]
            loginUser.setLevel(adMUser.getLevel());
            loginUser.setUuid(adMUser.getUuid());

            List<String> menuUrlList = saasPermissionExMapper.queryPermissionCodesOfUser(adMUser.getUserId() );
            loginUser.setMenuUrlList(menuUrlList);

            //---------------------------------------------------------------------------------------------------------數據權限BEGIN

            logger.info( "[獲取用戶的身份認證信息]="+loginUser);
            return new SimpleAuthenticationInfo(loginUser, authenticationToken.getCredentials()  ,  this.getName() );
        } catch (Exception e) {
            logger.error("獲取用戶的身份認證信息異常",e);
            return null;
        }
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SSOLoginUser loginUser = (SSOLoginUser) principalCollection.getPrimaryPrincipal();
        String account = loginUser.getLoginName(); //登錄名

        List<String> perms_string = saasPermissionExMapper.queryPermissionCodesOfUser(  loginUser.getId() );
        List<String> roles_string   = saasRoleExMapper.queryRoleCodesOfUser( loginUser.getId() );

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set<String> roles = new HashSet<String>( roles_string );
        authorizationInfo.setRoles( roles );
        logger.info( "[獲取用戶授權信息(角色)] "+account+"="+roles);

        Set<String> perms = new HashSet<String>( perms_string );
        authorizationInfo.setStringPermissions(perms);
        logger.info( "[獲取用戶授權信息(權限)] "+account+"="+perms);
        return authorizationInfo;
    }

    @Override
    public Object getAuthorizationCacheKey(PrincipalCollection principals) {
        SSOLoginUser loginUser = (SSOLoginUser) principals.getPrimaryPrincipal();
        String account = loginUser.getLoginName(); //登錄名
        return "-AuthInfo-"+account;
    }
    

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
}

大概思路是這樣。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM