0%

shiro-拦截器

可以处理每次请求的认证。

shiro-拦截器

介绍

需求:

  • 登录:

    • 是否请求跳转登录页面。
      • 是get的登录请求,返回登录页面。
      • 是post的登录请求(输入了账号密码,要登录了)。
      • 非登录请求,保存当前请求,重定向到登录页面。
  • 认证:

    1. 是否携带认证信息
      • 未携带认证信息,返回错误码
    2. 认证信息是否正确
    3. 执行拦截器链
      • 路径匹配

implementation

  1. 新建模块

    image-20201006212116681

  2. 导入maven

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xiaoruiit</groupId>
    <artifactId>chapter5-interceptor</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.9</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.3</version>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.2</version>
    </dependency>

    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.2</version>
    </dependency>

    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.25</version>
    </dependency>
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>0.2.23</version>
    </dependency>
    </dependencies>

    <build>
    <finalName>chapter5-interceptor</finalName>
    <plugins>
    <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.8.v20121106</version>
    <configuration>
    <webAppConfig>
    <contextPath>/${project.build.finalName}</contextPath>
    </webAppConfig>
    </configuration>
    </plugin>

    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
    <path>/${project.build.finalName}</path>
    </configuration>

    </plugin>
    </plugins>


    </build>
    </project>
  3. web.xml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0"
    metadata-complete="false">

    <!--- shiro 1.2 -->
    <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <context-param>
    <param-name>shiroEnvironmentClass</param-name>
    <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
    </context-param>
    <context-param>
    <param-name>shiroConfigLocations</param-name>
    <param-value>classpath:shiro-interceptor.ini</param-value>
    </context-param>
    <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    </web-app>
  4. shiro-interceptor.ini配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [users]
    zhang=123,admin
    wang=123

    [roles]
    admin=user:*,menu:*

    [filters]
    myFilter1=com.xiaoruiit.shiro.filter.MyOncePerRequestFilter
    myFilter2=com.xiaoruiit.shiro.filter.MyAdviceFilter
    myFilter3=com.xiaoruiit.shiro.filter.MyPathMatchingFilter
    myFilter4=com.xiaoruiit.shiro.filter.MyAccessControlFilter
    formLogin=com.xiaoruiit.shiro.filter.FormLoginFilter
    anyRoles=com.xiaoruiit.shiro.filter.AnyRolesFilter
    [urls]
    /test.jsp=formLogin,anyRoles[admin,user]
    /login.jsp=formLogin
    /**=myFilter1,myFilter2,myFilter3[config],myFilter4
    #/**=myFilter1
  5. 动态url拦截器

    MyIniWebEnviroment.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    package com.xiaoruiit.shiro.env;

    import org.apache.shiro.util.ClassUtils;
    import org.apache.shiro.web.env.IniWebEnvironment;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
    import org.apache.shiro.web.filter.mgt.DefaultFilter;
    import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
    import org.apache.shiro.web.filter.mgt.FilterChainResolver;
    import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;

    import javax.servlet.Filter;

    /**
    * @author hxr
    * @Classname MyIniWebEnviroment
    * @Description ToDo
    */
    public class MyIniWebEnviroment extends IniWebEnvironment {

    // 动态实现url-拦截器的注册。需要将其配置到web.xml中·
    @Override
    protected FilterChainResolver createFilterChainResolver() {
    //在此处扩展自己的FilterChainResolver
    //1、创建FilterChainResolver
    PathMatchingFilterChainResolver filterChainResolver =
    new PathMatchingFilterChainResolver();
    //2、创建FilterChainManager
    DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager();
    //3、注册Filter
    for(DefaultFilter filter : DefaultFilter.values()) {
    filterChainManager.addFilter(filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass()));
    }
    //4、注册URL-Filter的映射关系
    filterChainManager.addToChain("/login.jsp", "authc");
    filterChainManager.addToChain("/unauthorized.jsp", "anon");
    filterChainManager.addToChain("/**", "authc");
    filterChainManager.addToChain("/**", "roles", "admin");

    //5、设置Filter的属性
    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;
    // return super.createFilterChainResolver();
    }
    }
  6. form表单过滤器

    FormLoginFilter.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    package com.xiaoruiit.shiro.filter;

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.web.filter.PathMatchingFilter;
    import org.apache.shiro.web.util.WebUtils;

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    /**
    * @author hxr
    * @Classname FormLoginFilter
    * @Description ToDo
    */
    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;//已经登录过
    }
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;
    if(isLoginRequest(req)) {
    if("post".equalsIgnoreCase(req.getMethod())) {//form表单提交
    boolean loginSuccess = login(req); //登录
    if(loginSuccess) {
    return redirectToSuccessUrl(req, resp);
    }
    }
    return true;//继续过滤器链
    } else {//保存当前地址并重定向到登录界面
    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));
    }
    }
  7. AnyRolesFilter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package com.xiaoruiit.shiro.filter;

    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.StringUtils;
    import org.apache.shiro.web.filter.AccessControlFilter;
    import org.apache.shiro.web.util.WebUtils;

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;


    public class AnyRolesFilter extends AccessControlFilter {

    private String unauthorizedUrl = "/unauthorized.jsp";
    private String loginUrl = "/login.jsp";

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    String[] roles = (String[])mappedValue;
    if(roles == null) {
    return true;//如果没有设置角色参数,默认成功
    }
    for(String role : roles) {
    if(getSubject(request, response).hasRole(role)) {
    return true;
    }
    }
    return false;//跳到onAccessDenied处理
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    Subject subject = getSubject(request, response);
    if (subject.getPrincipal() == null) {//表示没有登录,重定向到登录页面
    saveRequest(request);
    WebUtils.issueRedirect(request, response, loginUrl);
    } else {
    if (StringUtils.hasText(unauthorizedUrl)) {//如果有未授权页面跳转过去
    WebUtils.issueRedirect(request, response, unauthorizedUrl);
    } else {//否则返回401未授权状态码
    WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }
    }
    return false;
    }
    }
  8. 自定义拦截器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package com.xiaoruiit.shiro.filter;

    import org.apache.shiro.web.servlet.OncePerRequestFilter;

    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.IOException;

    /**
    * @author hxr
    * @Classname MyOncePerRequestFilter
    * @Description
    * 自定义的拦截器实现扩展功能
    * 1.动态 url -角色/权限访问控制的实现
    * 2.根据 Subject 身份信息获取用户信息绑定到 Request
    * 3.验证码验证
    * 4.在线用户信息的保存
    * OncePerRequestFilter 保证一次请求只调用一次 doFilterInternal
    * 需要将其配置在ini中
    */
    public class MyOncePerRequestFilter extends OncePerRequestFilter {
    protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
    System.out.println("=========once per request filter");
    chain.doFilter(request, response);
    }
    }
  9. MyAdviceFilter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.xiaoruiit.shiro.filter;

    import org.apache.shiro.web.servlet.AdviceFilter;

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;

    /**
    * <p>User: Zhang Kaitao
    * <p>Date: 14-2-3
    * <p>Version: 1.0
    */
    public class MyAdviceFilter extends AdviceFilter {
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    System.out.println("====预处理/前置处理");
    return true;//返回false将中断后续拦截器链的执行
    }
    @Override
    protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
    System.out.println("====后处理/后置返回处理");
    }
    @Override
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
    System.out.println("====完成处理/后置最终处理");
    }
    }
  10. MyPathMatchingFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.xiaoruiit.shiro.filter;

import org.apache.shiro.web.filter.PathMatchingFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Arrays;

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;
}
}
  1. MyAccessControlFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.xiaoruiit.shiro.filter;

import org.apache.shiro.web.filter.AccessControlFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class MyAccessControlFilter extends AccessControlFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
System.out.println("access allowed");
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
System.out.println("访问拒绝也不自己处理,继续拦截器链的执行");
return true;
}
}
  1. 前端

    login.jsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>登录</title>
    <style>.error{color:red;}</style>
    </head>
    <body>

    <div class="error">${error}</div>
    <form action="" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="登录">
    </form>

    </body>
    </html>

    test.jsp

    1
    2
    3
    4
    5
    <html>
    <body>
    login success
    </body>
    </html>

测试

Console

1
2
3
4
5
6
=========once per request filter
====预处理/前置处理
url matches,config is [config]
access allowed
====后处理/后置返回处理
====完成处理/后置最终处理