Shiro Learning (8) interceptor mechanism

Source: Internet
Author: User

Shiro Learning (8) interceptor mechanism

8.1 Introduction to interceptor
Shiro uses the same Filter interface as Servlet for extension. If you are not familiar with the Filter, refer to Servlet3.1 specifications http://www.iteye.com/blogs/subjects/servlet-3-1to understand the working principle of filtering. The first is the basic class diagram of the Shiro Interceptor:

1. NameableFilter
NameableFilter gives the Filter a name. If it is not set, the default name is FilterName. Do you still remember the previous name, such as authc? When we assemble the interceptor chain, we will find the corresponding interceptor instance based on this name;

2. OncePerRequestFilter
OncePerRequestFilter is used to prevent multiple filters from being executed. That is to say, a request takes only one interceptor chain. In addition, the enabled attribute is provided to indicate whether to enable the interceptor instance. By default, enabled = true indicates that the interceptor instance is enabled, if you do not want an interceptor to work, set it to false.

3. ShiroFilter
ShiroFilter is the entry point of Shiro. It is used to intercept requests that require security control for processing. It has been used before.

4. AdviceFilter
AdviceFilter provides support for the AOP style, similar to Interceptor in SpringMVC:
Java code

boolean preHandle(ServletRequest request, ServletResponse response) throws Exception  void postHandle(ServletRequest request, ServletResponse response) throws Exception  void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception;   

PreHandler: similar to the pre-enhancement in AOP. It is executed before the interceptor chain is executed. If true is returned, the interceptor chain is resumed; otherwise, the subsequent interceptor chain execution is interrupted and directly returned; pre-processing (such as form-based authentication and authorization)
PostHandle: It is similar to the post-return enhancement in AOP; it is executed after the interceptor chain is executed; it is post-processed (such as recording the execution time );
AfterCompletion: similar to post-Final enhancement in AOP; that is, it will be executed no matter whether there are any exceptions; resources can be cleared (such as binding Subject to thread );

5. PathMatchingFilter
PathMatchingFilter provides the Ant-style Request Path Matching function and the interceptor Parameter Parsing function, for example, "roles [admin, user]" Automatically Based on ", "Split and parsed to a path parameter configuration and bound to the corresponding path:
Java code

boolean pathsMatch(String path, ServletRequest request)  boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception   

PathsMatch: This method is used to match a path with a request path. If yes, true is returned;
OnPreHandle: In preHandle, when pathsMatch matches a path, the opPreHandler method is called and the path binding parameter configuration is passed to mappedValue; then, you can perform some verification (such as Role authorization) in this method. If the verification fails, the system returns false to interrupt the process. The system returns true by default. That is, sub-classes can only implement onPreHandle without preHandle. If no path matches the Request path, The preHandle returns true by default ).

6. AccessControlFilter
AccessControlFilter provides basic access control functions, such as whether to allow access or how to handle access rejection:
Java code collection code

abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;   

IsAccessAllowed: Indicates whether access is allowed. mappedValue is part of the interceptor parameter in [urls] configuration. If access is allowed, true is returned; otherwise, false is returned;
OnAccessDenied: indicates whether the request has been processed when the access is rejected. If true is returned, the request must be processed. If false is returned, the request is returned.

OnPreHandle will automatically call these two methods to determine whether to continue processing:
Java code

boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {      return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);  }   

In addition, AccessControlFilter also provides the following method for processing, such as after Successful Logon/redirection to the previous request:
Java code

Void setLoginUrl (String loginUrl) // used for identity authentication. Default Value:/login. jsp String getLoginUrl () Subject getSubject (ServletRequest request, ServletResponse response) // obtain the Subject instance boolean isLoginRequest (ServletRequest request, ServletResponse response) // whether the current request is a Logon request void saveRequestAndRedirectToLogin (ServletRequest request, ServletResponse response) throws IOException // Save the current request and redirect it to the logon page void saveRequest (ServletRequest request) // Save the request. If the logon succeeds, the request void redirectToLogin (ServletRequest request, ServletResponse response) will be redirected back to the logon page.

For example, form-based authentication requires these features.

The basic interceptor is complete. If we want to control access, we can inherit AccessControlFilter. If we want to add some general data, we can inherit PathMatchingFilter directly.

8.2 interceptor chain
Shiro acts as a proxy for the Servlet container's FilterChain. That is, before ShiroFilter continues executing the Servlet container's Filter chain, it uses ProxiedFilterChain to proxy the Servlet container's FilterChain; shiro's own Filter system should be taken first, and then the Servlet container's FilterChain will be delegated for Servlet container-level Filter chain execution; Shiro's ProxiedFilterChain execution process: 1. First execute Shiro's own Filter chain; 2. then execute the Servlet container's Filter chain (that is, the original Filter ).
ProxiedFilterChain is parsed through FilterChainResolver based on whether the [urls] part of the configuration file matches the requested URL.
Java code

FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain);  

That is, pass in the original chain to get a proxy chain.
Shiro provides a path matching FilterChainResolver implementation: PathMatchingFilterChainResolver, which is based on the url mode configured in [urls] (default Ant style) = whether the interceptor chain matches the request url to parse the configured interceptor chain, while the PathMatchingFilterChainResolver maintains the interceptor chain through FilterChainManager, for example, DefaultFilterChainManager maintains the relationship between the url mode and the interceptor chain. Therefore, we can dynamically Add the relationship between url mode and interceptor chain through FilterChainManager.

DefaultFilterChainManager adds the interceptor declared in org. apache. shiro. web. filter. mgt. DefaultFilter by default:
Java code

public enum DefaultFilter {      anon(AnonymousFilter.class),      authc(FormAuthenticationFilter.class),      authcBasic(BasicHttpAuthenticationFilter.class),      logout(LogoutFilter.class),      noSessionCreation(NoSessionCreationFilter.class),      perms(PermissionsAuthorizationFilter.class),      port(PortFilter.class),      rest(HttpMethodPermissionFilter.class),      roles(RolesAuthorizationFilter.class),      ssl(SslFilter.class),      user(UserFilter.class);  }   

The next section describes the functions of these interceptors.

If you want to register a custom interceptor, IniSecurityManagerFactory/WebIniSecurityManagerFactory will automatically scan the [filters]/[main] section of the ini configuration file at startup and register the Interceptor to DefaultFilterChainManager; create a link between the url mode and the interceptor. If Spring is used, subsequent sections will introduce how to register a custom interceptor.

To customize FilterChainResolver, You can implement the WebEnvironment interface:
Java code

Public class extends IniWebEnvironment {@ Override protected FilterChainResolver createFilterChainResolver () {// expand your own FilterChainResolver return super. createFilterChainResolver ();}}

The relationship between FilterChain. If you want to dynamically implement url-blocker registration, you can implement the FilterChainResolver here, for example:
Java code

// 1. Create registrant FilterChainResolver = new registrant (); // 2. Create FilterChainManager DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager (); // 3. register the Filter for (DefaultFilter filter Filter filter: defaultFilter. values () {filterChainManager. addFilter (filter. name (), (Filter) ClassUtils. newInstance (filter. getFilterClass ();} // 4. register the filing relationship filterChainManager of URL-Filter. addToChain ("/login. jsp "," authc "); filterChainManager. addToChain ("/unauthorized. jsp "," anon "); filterChainManager. addToChain ("/**", "authc"); filterChainManager. addToChain ("/**", "roles", "admin"); // 5. Set the Filter attribute FormAuthenticationFilter authcFilter = (FormAuthenticationFilter) filterChainManager. getFilter ("authc"); authcFilter. setLoginUrl ("/login. jsp "); RolesAuthorizationFilter rolesFilter = (RolesAuthorizationFilter) filterChainManager. getFilter ("roles"); rolesFilter. setUnauthorizedUrl ("/unauthorized. jsp "); filterChainResolver. setFilterChainManager (filterChainManager); return filterChainResolver;

Register the filter and the ing between the url mode and the filter. You can customize FilterChainResolver or FilterChainManager to implement dynamic URL matching.

Then configure Environment in web. xml as follows:
Java code


    
   
    shiroEnvironmentClass
    
   
    com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment
     
  

8.3 custom interceptor
You can customize your own Interceptor to expand some functions, such as implementing dynamic url-Role/permission access control and getting user information based on Subject identity information and binding it to Request (that is, setting general data) verification code verification, online user information storage, etc., because it is essentially a Filter, so it can do what it can do.

For more information about filters, see the Filter section in Servlet specifications:
Http://www.iteye.com/blogs/subjects/servlet-3-1.

1. Extended OncePerRequestFilter
OncePerRequestFilter ensures that a single request only calls doFilterInternal once, that is, the internal forward will not execute doFilterInternal again:
Java code

public class MyOncePerRequestFilter extends OncePerRequestFilter {      @Override      protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {          System.out.println("=========once per request filter");          chain.doFilter(request, response);      }  }   

Then, in the shiro. ini configuration file:
Java code

[main]  myFilter1=com.github.zhangkaitao.shiro.chapter8.web.filter.MyOncePerRequestFilter  #[filters]  #myFilter1=com.github.zhangkaitao.shiro.chapter8.web.filter.MyOncePerRequestFilter  [urls]  /**=myFilter1  

Filter can be registered in [main] or [filters], and then configure the ing between url and filter in [urls.

2. Extended AdviceFilter
AdviceFilter provides the AOP function, which achieves the same idea as Interceptor in SpringMVC: For details, refer to the processor Interceptor section in my SpringMVC Tutorial:
Http://www.iteye.com/blogs/subjects/kaitao-springmvc
Java code

Public class MyAdviceFilter extends AdviceFilter {@ Override protected boolean preHandle (ServletRequest request, ServletResponse response) throws Exception {System. out. println ("=== pre-processing/pre-processing"); return true; // if false is returned, subsequent interceptor chain execution will be interrupted.} @ Override protected void postHandle (ServletRequest request, servletResponse response) throws Exception {System. out. println ("=== post-processing/post-return processing");} @ Override public void afterCompletion (ServletRequest request, ServletResponse response, Exception exception) throws Exception {System. out. println ("==== completed/post-processed ");}}

PreHandle: Pre-processes the request and determines whether to continue processing based on the returned value (true: Continue filter chain); permission control can be implemented through it;
PostHandle: After the interceptor chain is executed, it is executed after normal return;
AfterCompletion: whether or not there is an exception, afterCompletion will be executed to complete the resource clearing function.

Configure shiro. ini as follows:
Java code

[filters]  myFilter1=com.github.zhangkaitao.shiro.chapter8.web.filter.MyOncePerRequestFilter  myFilter2=com.github.zhangkaitao.shiro.chapter8.web.filter.MyAdviceFilter  [urls]  /**=myFilter1,myFilter2   

For more information about how to use this filter, see the processor interceptor section in my SpringMVC tutorial.

3. PathMatchingFilter
PathMatchingFilter inherits the AdviceFilter and provides the url filter function. to process a specified request, you can extend PathMatchingFilter:
Java code collection code

public class MyPathMatchingFilter extends PathMatchingFilter {      @Override      protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {         System.out.println("url matches,config is " + Arrays.toString((String[])mappedValue));         return true;      }  }   

PreHandle: matches the url mode with the requested url. If yes, onPreHandle is called. If no url mode or url mode is configured, true is returned by default;
OnPreHandle: If the url mode matches the request url, onPreHandle is executed and the parameters configured by the interceptor are passed in. By default, true is returned directly if nothing is not processed.

Configure shiro. ini as follows:
Java code

[filters]  myFilter3=com.github.zhangkaitao.shiro.chapter8.web.filter.MyPathMatchingFilter  [urls]  /**= myFilter3[config]   

/** Is the url mode registered to PathMatchingFilter. config is the configuration parameters of the Interceptor. Multiple parameters are separated by commas. onPreHandle uses mappedValue to receive parameter values.

4. Extended AccessControlFilter
AccessControlFilter inherits PathMatchingFilter and extends the following two methods:
Java code

public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {      return isAccessAllowed(request, response, mappedValue)       || onAccessDenied(request, response, mappedValue);  }   

IsAccessAllowed: Indicates whether access is allowed. true Indicates access is allowed;
OnAccessDenied: Indicates whether to process the access request by yourself. If true is returned, it means that the request is not processed and the interceptor chain is executed. If false is returned, it means that the request has been processed (for example, redirected to another page ).

Java code

Public class MyAccessControlFilter extends AccessControlFilter {protected boolean isAccessAllowed (ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {System. out. println ("access allowed"); return true;} protected boolean onAccessDenied (ServletRequest request, ServletResponse response) throws Exception {System. out. println ("the access is denied and cannot be handled by yourself. Continue to execute the interceptor chain"); return true ;}}

Configure shiro. ini as follows:
Java code

[filters]  myFilter4=com.github.zhangkaitao.shiro.chapter8.web.filter.MyAccessControlFilter  [urls]  /**=myFilter4  

5. form-based login interceptor
Previously, we have used the Shiro built-in form-based login Interceptor. Here we will build a similar form-based login interceptor.
Java code

Public class FormLoginFilter extends PathMatchingFilter {private String loginUrl = "/login. jsp "; private String successUrl ="/"; @ Override protected boolean onPreHandle (ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {if (SecurityUtils. getSubject (). isAuthenticated () {return true; // already logged on} HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (isLoginRequest (req )) {if ("post ". equalsIgnoreCase (req. getMethod () {// form submission boolean loginSuccess = login (req); // Log On if (loginSuccess) {return redirectToSuccessUrl (req, resp) ;}} return true; // continue filter chain} else {// Save the current address and redirect to the logon interface saveRequestAndRedirectToLogin (req, resp); return false ;}} private boolean redirectToSuccessUrl (HttpServletRequest req, HttpServletResponse resp) throws IOException {WebUtils. redirectToSavedRequest (req, resp, successUrl); return false;} private void saveRequestAndRedirectToLogin (HttpServletRequest req, HttpServletResponse resp) throws IOException {WebUtils. saveRequest (req); WebUtils. issueRedirect (req, resp, loginUrl);} private boolean login (HttpServletRequest req) {String username = req. getParameter ("username"); String password = req. getParameter ("password"); try {SecurityUtils. getSubject (). login (new UsernamePasswordToken (username, password);} catch (Exception e) {req. setAttribute ("shiroLoginFailure", e. getClass (); return false;} return true;} private boolean isLoginRequest (HttpServletRequest req) {return pathsMatch (loginUrl, WebUtils. getPathWithinApplication (req ));}}

OnPreHandle main process:
1. First, determine whether you have logged on. If you have logged on, continue with the interceptor chain;
2. If you have not logged on, check whether it is a logon request. If it is a get method login page request, continue the interceptor chain (to the request page ), otherwise, if it is another page request of the get method, save the current request and redirect it to the logon page;
3. If the post method is used to submit the login page form, you can collect the username/password for Logon. If the logon fails, save the error message to "shiroLoginFailure" and return to the login page;
4. If the login succeeds and a previously saved request exists, redirect the request to the previous one. Otherwise, the default success page is displayed.

Shiro. ini configuration
Java code

[filters]  formLogin=com.github.zhangkaitao.shiro.chapter8.web.filter.FormLoginFilter  [urls]  /test.jsp=formLogin  /login.jsp=formLogin   

Start the server and enter http: // localhost: 8080/chapter8/test. jsp for testing. The logon page is automatically displayed, and the test. jsp page is displayed after successful logon.

In this example, we can inherit the AuthenticatingFilter, which provides a lot of basic logon code. In addition, you can refer to the source code of FormAuthenticationFilter embedded in Shiro. The idea is the same.

6. Any role authorization interceptor
Shiro provides the roles interceptor, which verifies that the user has all roles and does not provide the interceptor that verifies that the user has any roles.
Java code

Public class AnyRolesFilter extends AccessControlFilter {private String unauthorizedUrl = "/unauthorized. jsp "; private String loginUrl ="/login. jsp "; protected boolean isAccessAllowed (ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {String [] roles = (String []) mappedValue; if (roles = null) {return true; // if no role parameter is set, the default value is successful.} for (String role: roles) {if (getSubject (request, response ). hasRole (role) {return true ;}} return false; // jump to onAccessDenied for processing} @ Override protected boolean onAccessDenied (ServletRequest request, ServletResponse response) throws Exception {Subject subject = getSubject (request, response); if (subject. getPrincipal () = null) {// indicates no logon. Redirect to the logon page saveRequest (request); WebUtils. issueRedirect (request, response, loginUrl);} else {if (StringUtils. hasText (unauthorizedUrl) {// If an unauthorized page exists, redirect to WebUtils. issueRedirect (request, response, unauthorizedUrl);} else {// otherwise, the system returns the 401 unauthorized status code WebUtils. toHttp (response ). sendError (HttpServletResponse. SC _UNAUTHORIZED) ;}} return false ;}}

Process:
1. First, determine whether the user has any role. If false is not returned, the onAccessDenied will be used for processing;
2. If the user does not have a role, determine whether the user has logged on. If the user does not have a logon role, first redirect to the logon role;
3. If the user does not have a role and has an unauthorized page (unauthorizedUrl) configured, the user is redirected to the unauthorized page; otherwise, the error code 401 is returned.

Shiro. ini configuration
Java code

[filters]  anyRoles=com.github.zhangkaitao.shiro.chapter8.web.filter.AnyRolesFilter  [urls]  /test.jsp=formLogin,anyRoles[admin,user]  /login.jsp=formLogin   

The AuthorizationFilter can be inherited here, and it provides the basic authorization code. In addition, you can refer to the source code of the built-in RolesAuthorizationFilter in Shiro to implement the hasAllRoles logic.

8.4 default interceptor
Shiro has many built-in default interceptors, such as identity authentication and authorization. For the default interceptor, refer to the enumeration interceptor in org. apache. shiro. web. filter. mgt. DefaultFilter:

In addition, an org. apache. shiro. web. filter. authz. HZ plugin is provided? Http://www.bkjia.com/kf/ware/vc/ "target =" _ blank "class =" keylink "> vc3RGaWx0ZXKjrLy01ve7 + keys/cS/keys/ydPDoaM8L3A + keys" brush: java; ">perms.unauthorizedUrl=/unauthorized

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.