Shiro 소스 코드 분석 --- 인증 절차
18948 단어 Shiro
Shiro 와 Spring 이 통합 되 었 을 때 웹. xml 에 Shiro 입구 필 터 를 설정 해 야 합 니 다.
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
true
targetFilterLifecycle
true
shiroFilter
/*
Spring 에 익숙 한 사람들 은 Delegating Filter Proxy 의 역할 을 알 고 있 을 것 이다. 이 Spring 이 제공 하 는 필 터 는 의뢰 역할 만 하고 실행 절 차 는 Spring 용기 에 shiro Filter 라 는 필터 에 위탁 된다.그래서 Spring 프로필 에 shiroFilter 를 설정 해 야 합 니 다. 다음 과 같 습 니 다.
/index.jsp = anon
/unauthorized.jsp = anon
/login.jsp = authc
/logout = logout
/authenticated.jsp = authc
/** = user
ShiroFilterFactory Bean 은 org. springframework. beans. factory. Factory Bean 인 터 페 이 스 를 실 현 했 기 때문에 shiroFilter 대상 은 ShiroFilterFactory Bean 의 getObject () 방법 으로 되 돌 아 왔 습 니 다.
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance.");
//
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
// Web
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
//
FilterChainManager manager = createFilterChainManager();
//
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
// SpringShiroFilter
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
상기 소스 코드 에서 볼 수 있 듯 이 최종 적 으로 SpringShiroFilter 대상, 즉 Spring 설정 파일 의 shiroFilter 대상 을 되 돌려 주 었 습 니 다. 이 필 터 는 세 가지 중요 한 대상 을 가지 고 있 습 니 다: Security Manager, PathMatchingFilterChain Resolver, FilterChain Manager.Spring 설정 에 filter Chain Definitions 속성 이 설정 되 어 있 기 때문에 setFilterChain Definitions 방법 을 호출 합 니 다.
public void setFilterChainDefinitions(String definitions) {
Ini ini = new Ini();
ini.load(definitions);
//did they explicitly state a 'urls' section? Not necessary, but just in case:
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
//no urls section. Since this _is_ a urls chain definition property, just assume the
//default section contains only the definitions:
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
/** section,
/index.jsp = anon
/unauthorized.jsp = anon
/login.jsp = authc
/logout = logout
/authenticated.jsp = authc
/** = user
, URL Filter, anon、authc、logout Filter ,
Ini.Section Map , key URL ,value Filter
**/
// filterChainDefinitionMap
setFilterChainDefinitionMap(section);
}
FilterChainManager 는 현재 Shiro 응용 프로그램의 모든 Filter 를 관리 하 는 데 사 용 됩 니 다. Shiro 가 기본적으로 사용 하 는 Filter 가 있 고 사용자 정의 Filter 일 수도 있 습 니 다.FilterChain Manager 가 어떻게 만 들 어 졌 는 지 살 펴 보 겠 습 니 다.
protected FilterChainManager createFilterChainManager() {
// DefaultFilterChainManager
DefaultFilterChainManager manager = new DefaultFilterChainManager();
// Shiro Filter, org.apache.shiro.web.filter.mgt.DefaultFilter
Map defaultFilters = manager.getFilters();
//apply global settings if necessary:
for (Filter filter : defaultFilters.values()) {
// Filter loginUrl、successUrl、unauthorizedUrl
applyGlobalPropertiesIfNecessary(filter);
}
// Spring Filter
Map filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
// Filter , Filter Filter
manager.addFilter(name, filter, false);
}
}
//build up the chains:
Map chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
// URL FilterChain ,
// URL , URL URL Filter
// URL , , ,
manager.createChain(url, chainDefinition);
}
}
return manager;
}
PathMatchingFilterChain Resolver 대상 의 직책 은 간단 합 니 다. ant 경로 매 칭 방법 으로 방문 한 URL 과 일치 하 는 것 입 니 다. pathMatchingFilterChain Resolver 는 FilterChain Manager 대상 을 가지 고 있 기 때문에 URL 이 일치 하면 이 URL 에 적용 해 야 할 FilterChain 을 얻 을 수 있 습 니 다.상기 분석 을 통 해 알 수 있 듯 이 Shiro 는 일련의 URL 일치 자 를 통 해 URL 에 적용 해 야 할 Filter 를 설정 한 다음 Filter 에서 해당 하 는 작업 을 수행 하기 때문에 Shiro 의 모든 기능 은 Filter 를 통 해 이 루어 집 니 다.물론 인증 기능 도 예외 가 아니다. 상기 설정 에서 인증 기능 은 org. apache. shiro. web. filter. authc. FormAuthenticationFilter 에 의 해 이 루어 졌 다.입구 필터 SpringShiroFilter 의 실행 절 차 를 살 펴 보 겠 습 니 다. FormAuthenticationFilter 까지 어떻게 실행 되 는 지 살 펴 보 겠 습 니 다.Filter 라면 가장 중요 한 것 은 doFilter 방법 입 니 다. SpringShiroFilter 는 Once PerRequestFilter 에서 계승 되 기 때문에 doFilter 방법 도 Once PerRequestFilter 에서 정 의 됩 니 다.
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// Filter
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {
log.trace("Filter '{}' already executed. Proceeding without invoking this filter.", getName());
filterChain.doFilter(request, response);
} else //noinspection deprecation
if (/* added in 1.2: */ !isEnabled(request, response) ||
/* retain backwards compatibility: */ shouldNotFilter(request) ) {
log.debug("Filter '{}' is not enabled for the current request. Proceeding without invoking this filter.",
getName());
filterChain.doFilter(request, response);
} else {
// Do invoke this filter...
log.trace("Filter '{}' not yet executed. Executing now.", getName());
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
//
doFilterInternal(request, response, filterChain);
} finally {
// Once the request has finished, we're done and we don't
// need to mark as 'already filtered' any more.
request.removeAttribute(alreadyFilteredAttributeName);
}
}
}
doFilter 내부 방법 정의 AbstractShiroFilter 중:
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
// Subject , , , createSubject
final Subject subject = createSubject(request, response);
// Subject ,
subject.execute(new Callable() {
public Object call() throws Exception {
// ,
updateSessionLastAccessTime(request, response);
//
executeChain(request, response, chain);
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}
// ...
}
Subject 가 어떻게 만 들 어 졌 는 지 살 펴 보 겠 습 니 다.
protected WebSubject createSubject(ServletRequest request, ServletResponse response) {
return new WebSubject.Builder(getSecurityManager(), request, response).buildWebSubject();
}
추적 코드 최종 호출 DefaultWebSubjectFactory. createSubject 방법:
public Subject createSubject(SubjectContext context) {
if (!(context instanceof WebSubjectContext)) {
return super.createSubject(context);
}
WebSubjectContext wsc = (WebSubjectContext) context;
SecurityManager securityManager = wsc.resolveSecurityManager();
Session session = wsc.resolveSession();
boolean sessionEnabled = wsc.isSessionCreationEnabled();
PrincipalCollection principals = wsc.resolvePrincipals();
// , , false
boolean authenticated = wsc.resolveAuthenticated();
String host = wsc.resolveHost();
ServletRequest request = wsc.resolveServletRequest();
ServletResponse response = wsc.resolveServletResponse();
return new WebDelegatingSubject(principals, authenticated, host, session, sessionEnabled,
request, response, securityManager);
}
다음은 필터 체인 이 어떻게 만 들 고 실행 되 는 지 보 겠 습 니 다.
protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain)
throws IOException, ServletException {
// URL
FilterChain chain = getExecutionChain(request, response, origChain);
//
chain.doFilter(request, response);
}
protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
// , PathMatchingFilterChainResolver
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
// getChain , URL
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}
return chain;
}
상기 Spring 설정 에 따 르 면 현재 URL: "/ authenticated. jsp" 를 처음 방문 했다 고 가정 하면 authc 라 는 Filter, 즉 FormAuthenticationFilter 를 사용 합 니 다. FormAuthenticationFilter 의 계승 체계 에 따라 dviceFilter. do Filter Internal 방법 을 먼저 실행 합 니 다.
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
Exception exception = null;
try {
// preHandle
boolean continueChain = preHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
}
// preHandle false
if (continueChain) {
executeChain(request, response, chain);
}
postHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked postHandle method");
}
} catch (Exception e) {
exception = e;
} finally {
cleanup(request, response, exception);
}
}
다음 실행: PathMatchingFilter. preHandle 방법:
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
if (this.appliedPaths == null || this.appliedPaths.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("appliedPaths property is null or empty. This Filter will passthrough immediately.");
}
return true;
}
for (String path : this.appliedPaths.keySet()) {
// , URL:"/authenticated.jsp" , FormAuthenticationFilter,
// FormAuthenticationFilter PathMatchingFilter, true
if (pathsMatch(path, request)) {
log.trace("Current requestURI matches pattern '{}'. Determining filter chain execution...", path);
Object config = this.appliedPaths.get(path);
// isFilterChainContinued , onPreHandle
return isFilterChainContinued(request, response, path, config);
}
}
//no path matched, allow the request to go through:
return true;
}
이어서 AccessControlFilter. onPreHandle 방법 을 실행 합 니 다.
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
// isAccessAllowed false, onAccessDenied
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
다음 에 Authenticating Filter. is Access Allowed 방법 을 실행 합 니 다.
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return super.isAccessAllowed(request, response, mappedValue) ||
(!isLoginRequest(request, response) && isPermissive(mappedValue));
}
super.isAccessAllowed , AuthenticationFilter.isAccessAllowed :
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Subject subject = getSubject(request, response);
return subject.isAuthenticated();
}
위 코드 를 통 해 알 수 있 듯 이 URL 을 처음 방문 한 URL 입 니 다. "/ authenticated. jsp" 이기 때문에 isAccessAllowed 방법 은 false 로 되 돌아 갑 니 다. 따라서 FormAuthenticationFilter. onAccessDenied 방법 을 실행 합 니 다.
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//
if (isLoginRequest(request, response)) {
// POST
if (isLoginSubmission(request, response)) {
if (log.isTraceEnabled()) {
log.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (log.isTraceEnabled()) {
log.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (log.isTraceEnabled()) {
log.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
//
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
// request session , URL
saveRequest(request);
// , :"/login.jsp"
redirectToLogin(request, response);
}
설정 에 따라 URL: "/ login. jsp" 에 접근 할 때 도 FormAuthenticationFilter 를 적용 합 니 다. 방향 을 바 꾸 는 것 이기 때문에 GET 요청 을 했 기 때문에 isLogin Submission () 은 false 로 돌아 가기 때문에 executeLogin 방법 을 실행 하지 않 았 기 때문에 / login. jsp 페이지 에 접근 할 수 있 습 니 다.로그 인 폼 에 action = "을 설정 해 야 합 니 다. 로그 인 요청 은 / login. jsp 에 제출 됩 니 다. 이 때 는 POST 요청 이 므 로 executeLogin 방법 을 실행 합 니 다.
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
// AuthenticationToken
AuthenticationToken token = createToken(request, response);
if (token == null) {
String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
"must be created in order to execute a login attempt.";
throw new IllegalStateException(msg);
}
try {
// Subject
Subject subject = getSubject(request, response);
// Subject.login
subject.login(token);
// , URL
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
// , request,
return onLoginFailure(token, e, request, response);
}
}
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
// URL
issueSuccessRedirect(request, response);
// false,
return false;
}
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e,
ServletRequest request, ServletResponse response) {
// request
setFailureAttribute(request, e);
// true, ,
return true;
}
이로써 인증 절 차 는 대체적으로 이 렇 습 니 다. 지면 에 국한 되 고 로그 인 절차 가 구체 적 이 니 다음 박문 을 기대 하 십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[Shiro] Shiro 라벨 사용guest , , 。--> <shiro:guest> shiro:guest> <shiro:user> shiro:user> <--! authenticated , , Subject.login , 。...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.