Shiro 시스템 권한 관리 및 원리 분석
자주 사용 하 는 자바 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;
}
}