Shiro 시스템 권한 관리 및 원리 분석

1. 프로필      
        자주 사용 하 는 자바 EE 보안 프레임 워 크 는 shiro, spring 이 있 습 니 다. security。shiro 는 매우 광범 위 하 게 응용 되 어 카 스 를 통합 하여 단일 로그 인 시스템 을 구축 할 수 있 습 니 다.spring security 는 비교적 무 거 운 것 으로 여 겨 지 며, 응용 은 shiro 가 광범 위 하지 않다.shiro 는 사용자 이름, 비밀번호 검증, 암호 화 저장 소, 세 션 세 션 관리, 웹 통합 을 제공 하여 HTTPS 차단 을 지원 합 니 다.
2. Shiro 원리 에 대한 분석
shiro 원리 분석:
       shiro 의 핵심 은 자바 servlet 규범 중의 filter 입 니 다. 차단 기 를 설정 하고 차단기 체인 을 사용 하여 요청 을 차단 합 니 다. 접근 이 허용 되면 통과 합 니 다.일반적으로 시스템 로그 인, 종료 시 차단 기 를 설정 합 니 다.로그 인 할 때 subject. login (token) 을 호출 합 니 다. token 은 사용자 인증 정보 입 니 다. 이 때 Realm 에서 doGetAuthenticationInfo 방법 에서 인증 합 니 다.이 때 사용자 가 제출 한 인증 정 보 를 데이터베이스 에 저 장 된 인증 정보 와 비교 하고 일치 하면 방문 을 허용 하 며 브 라 우 저 에 이번 답장 의 쿠키 를 심 어 서버 에 session 정 보 를 저장 합 니 다.종료 할 때 subject. logout () 을 호출 하면 답장 메 시 지 를 지 웁 니 다.
      shiro 의 핵심 개념 소개:
         Filter:
                 1. AnonymousFilter: 이 filter 를 통 해 수 정 된 url 은 권한 인증 이 없 더 라 도 누구나 접근 할 수 있 습 니 다.
                 2. FormAuthenticationFilter: 이 filter 를 통 해 수 정 된 url 은 요청 한 url 을 검증 하고 통과 하지 않 으 면 loginurl 로 되 돌아 갑 니 다.
                 3. Basic Http AuthenticationFilter: 이 filter 를 통 해 수 정 된 url 은 사용자 에 게 인증 을 통 과 했 으 며, 통과 하지 않 으 면 Authorization 정 보 를 통 해 인증 을 요구 합 니 다.
                 4. LogoutFilter: 이 filter 를 통 해 수 정 된 url 은 url 요청 을 받 으 면 subject 를 즉시 호출 하여 종료 하고 redirectUrl 로 다시 설정 합 니 다.
                 5. NoSession CreationFilter: 이 filter 를 통 해 수 정 된 url 은 세 션 을 만 들 지 않 습 니 다.
                 6. PermissionAuthorizationFilter: 권한 차단기, 사용자 가 관련 권한 이 있 는 지 검증
                 7. PortFilter: 포트 차단 기 는 포트 를 만들어 url 에 접근 하 는 것 이 아니 라 자동 으로 포트 를 지정 한 포트 로 재 설정 합 니 다.
                 8. HttpMethodPermission Filter: rest 스타일 차단기, rest 접근 방식 설정
                 9. RolesAuthorizationFilter: 캐릭터 차단기, 로그 인하 지 않 음, loginurl 로 이동, 권한 이 부여 되 지 않 음, unauthorizedUrl 로 이동
                 10. SslFilter: HTTPS 차단기, HTTPS 방식 으로 접근 해 야 합 니 다.
                 11. UserFilter: 사용자 차단기, 사용자 인증 이 필요 하거나 remember me
         Subject:
                 현재 작업 의 주체 사용 자 를 나타 내 는 추상 적 인 개념 으로 Subject 는 로그 인, 종료, 권한 판단 등 동작 을 할 수 있 으 며 보통 하나의 subject 는 하나의 스 레 드 와 연 결 됩 니 다.
         Realm:
                검 증 된 데이터 원본 을 표시 하고 사용자 의 안전 데 이 터 를 저장 하 며 사용자 이름과 암호 가 일치 하고 사용자 권한 조 회 를 할 수 있 습 니 다.
3. Shiro 설정 방법
shiro 설정 방법:



    Apache Shiro Security Configuration

    
    
        
    

    
    

    
    
        
        
        
        
        
    

    
    
        
        
    

    
    
        
        
        
        
        
    

    
    
        
        
        
    

    
    
        
        
    

    
    

    
    
        
        
        
            
                
                
            
        
        
            
                /** = auth
            
        
    

    
    

    
    
        
    

    
    
        
        
        
        
    


 
   Pom 의존:
    1.2.2
    2.4.2.1-RELEASE

			org.apache.shiro
			shiro-core
			${shiro.version}
		
		
			org.apache.shiro
			shiro-web
			${shiro.version}
		
		
			org.apache.shiro
			shiro-spring
			${shiro.version}
		
		
			org.crazycake
			shiro-redis
			${shiro.redis.version}
		

    
UserRealm:
    Authorizing Realm 을 계승 하여 doGetAuthorization 과 doGetAuthorization 방법 을 다시 씁 니 다.
 
  
@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        SysUser user = getUserByName(username);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        SysRole sysRole = sysRolePermService.getSysRoleByUserId(user.getId());
        addRole(authorizationInfo,sysRole);
        List sysPermissions = sysRolePermService.getSysPermissionByUserId(user.getId());
        addPermissions(authorizationInfo,sysPermissions);
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String username = (String)token.getPrincipal();

        SysUser user = getUserByName(username);

        //  AuthenticatingRealm  CredentialsMatcher      ,                
        if (user != null){
            return new SimpleAuthenticationInfo(user.getUserName(),"", getName());
        }
        return null;
    }

    
AuthFilter:
AuthFilter 계승 AccessController Filter, isAccessAllowed 와 onAccessDenied 방법 을 다시 써 야 합 니 다.
@Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpResponse = (HttpServletResponse)response;

        //    
        supportCrossDomain(httpResponse);

        //   API,  Service-Token    
        String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
        if (url.startsWith(HTTP_API_URL)) {
            if (validateSign(httpRequest.getHeader("Service-Token"))) {
                return true;
            }
            else {
                return false;
            }
        }

        //      cookie,token,sid    
        Subject currentSubject = SecurityUtils.getSubject();
        if (! currentSubject.isAuthenticated()){
            SysUser sysTokenUser = loginAuthService.validTokenAuth((HttpServletRequest) request);
            SysUser sysCookieUser = loginAuthService.validCookieAuth((HttpServletRequest) request);

            if (sysTokenUser == null && sysCookieUser == null){
                return false;
            }
            SysUser sysUser = null;
            if (sysTokenUser != null){
                sysUser = sysTokenUser;
            }else {
                sysUser = sysCookieUser;
            }

            UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getUserName(),"");
            currentSubject.login(token);
            Session session = currentSubject.getSession();
            session.setAttribute("LoginUser",sysUser);
        }

        // request       
        SysUser currentUser = (SysUser) currentSubject.getSession().getAttribute("LoginUser");
        request.setAttribute("loginId", currentUser.getId());

        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(json);

        return false;
    }

    
RedisManager:
shiro 원생 은 redis 를 지원 하지 않 고 ehcache 만 지원 합 니 다. ehcache 는 단일 캐 시 만 지원 할 수 있 고 대형 클 러 스 터 응용 에 적합 하지 않 습 니 다.제3자 crazycake 는 shiro 가 redis 캐 시 를 사용 하 는 것 을 지원 합 니 다. 설정 방식 은 다음 과 같 습 니 다.
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Set;

public class RedisManager extends org.crazycake.shiro.RedisManager {

	private String host = "127.0.0.1";

	private int port = 6379;

	// 0 - never expire
	private int expire = 0;

	// timeout for jedis try to connect to redis server, not expire time! In
	// milliseconds
	private int timeout = 0;

	private String password = "";

	private int dataBase;

	private static JedisPool jedisPool = null;

	public RedisManager() {

	}

	/**
	 *      
	 */
	public void init() {
		if (jedisPool == null) {
			if (password != null && "".equals(password.trim())) {
				password = null;
			}
			jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password, dataBase);
		}
	}

	/**
	 * get value from redis
	 * 
	 * @param key
	 * @return
	 */
	public byte[] get(byte[] key) {
		byte[] value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			value = jedis.get(key);
		} finally {
			jedis.close();
		}
		return value;
	}

	/**
	 * set
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public byte[] set(byte[] key, byte[] value) {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.set(key, value);
			if (this.expire != 0) {
				jedis.expire(key, this.expire);
			}
		} finally {
			jedis.close();
		}
		return value;
	}

	/**
	 * set
	 * 
	 * @param key
	 * @param value
	 * @param expire
	 * @return
	 */
	public byte[] set(byte[] key, byte[] value, int expire) {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.set(key, value);
			if (expire != 0) {
				jedis.expire(key, expire);
			}
		} finally {
			jedis.close();
		}
		return value;
	}

	/**
	 * del
	 * 
	 * @param key
	 */
	public void del(byte[] key) {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.del(key);
		} finally {
			jedis.close();
		}
	}

	/**
	 * flush
	 */
	public void flushDB() {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.flushDB();
		} finally {
			jedis.close();
		}
	}

	/**
	 * size
	 */
	public Long dbSize() {
		Long dbSize = 0L;
		Jedis jedis = jedisPool.getResource();
		try {
			dbSize = jedis.dbSize();
		} finally {
			jedis.close();
		}
		return dbSize;
	}

	/**
	 * keys
	 * 
	 * @param pattern
	 * @return
	 */
	public Set keys(String pattern) {
		Set keys = null;
		Jedis jedis = jedisPool.getResource();
		try {
			keys = jedis.keys(pattern.getBytes());
		} finally {
			jedis.close();
		}
		return keys;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public int getExpire() {
		return expire;
	}

	public void setExpire(int expire) {
		this.expire = expire;
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getDataBase() {
		return dataBase;
	}

	public void setDataBase(int dataBase) {
		this.dataBase = dataBase;
	}

}

좋은 웹페이지 즐겨찾기