shiro主要用于web开发环境下,本小节学习web环境下的应用
1.准备工作
添加jar包,先前都是添加的core包,web环境下需要shiro-web,同时为了便于开发,同时还需要servlet,jsp等jar包
<!-- javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<!-- commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
同时还需要添加log4j的配置文件
2.启动shiro
web环境下初始化shiro,需要在web.xml文件中添加拦截器和监听器
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<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>
同时还需要添加shiro的配置文件:shiro.ini,文件路径可以:1./WEB-INF/shiro.ini 或者 2. 根目录下
如果没有配置文件,启动就会报错。
自定义路径:
<filter>
<filter-name>ShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
<init-param>
<param-name>configPath</param-name>
<param-value>/WEB-INF/anotherFile.ini</param-value>
</init-param>
</filter>
修改
<param-value>/WEB-INF/anotherFile.ini</param-value>
可以自定义路径
详情可见http://shiro.apache.org/web.html#Web-RememberMeServices
3.web下INI配置
ini配置文件中有四个部分:[main]、[users]、[roles]、[urls]
[urls]为应用程序中任何匹配的URL定义自定义的过滤器
例子;
[urls]
/index.html = anon
/user/create = anon
/user/** = authc
/admin/** = authc, roles[administrator]
/rest/** = authc, rest
/remoting/rpc/** = authc, perms["remote:invoke"]
等号左边的是要拦截的URL,等号右边则是过滤器
shiro的过滤器支持自定义过滤器,通过自己定义的过滤器java代码,在ini配置文件中声明。
可以参考http://jinnianshilongnian.iteye.com/blog/2025656
这里主要讲shiro提供的默认过滤器
参考于http://jinnianshilongnian.iteye.com/blog/2025656
Shiro内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举拦截器:
默认拦截器名 |
拦截器类 |
说明(括号里的表示默认值) |
身份验证相关的 |
||
authc |
org.apache.shiro.web.filter.authc .FormAuthenticationFilter |
基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录;主要属性:usernameParam:表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe); loginUrl:登录页面地址(/login.jsp);successUrl:登录成功后的默认重定向地址; failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure); |
authcBasic |
org.apache.shiro.web.filter.authc .BasicHttpAuthenticationFilter |
Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application); |
logout |
org.apache.shiro.web.filter.authc .LogoutFilter |
退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout” |
user |
org.apache.shiro.web.filter.authc .UserFilter |
用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user” |
anon |
org.apache.shiro.web.filter.authc .AnonymousFilter |
匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon” |
授权相关的 |
||
roles |
org.apache.shiro.web.filter.authz .RolesAuthorizationFilter |
角色授权拦截器,验证用户是否拥有所有角色;主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]” |
perms |
org.apache.shiro.web.filter.authz .PermissionsAuthorizationFilter |
权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms["user:create"]” |
port |
org.apache.shiro.web.filter.authz .PortFilter |
端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样 |
rest |
org.apache.shiro.web.filter.authz .HttpMethodPermissionFilter |
rest风格拦截器,自动根据请求方法构建权限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll); |
ssl |
org.apache.shiro.web.filter.authz .SslFilter |
SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样; |
其他 |
||
noSessionCreation |
org.apache.shiro.web.filter.session .NoSessionCreationFilter |
不创建会话拦截器,调用 subject.getSession(false)不会有什么问题,但是如果 subject.getSession(true)将抛出 DisabledSessionException异常; |
定义自己的shiro.ini文件
[main]
#身份认证没有成功跳转这个URL
authc.loginUrl = /login
#角色认证未通过
roles.unauthorizedUrl=/unauthorized.jsp
#权限认证未通过
perms.unauthorizedUrl=/unauthorized.jsp
[users]
liy313=123456,admin
jack=123,teacher
mary=123
[roles]
admin=user:*
teacher=student:*
[urls]
#请求login不需要身份认证,不然进入死循环
/login=anon
#进入比较敏感的URL需要身份认证
/admin=authc
/student=roles[teacher]
/teacher=perms["user:create"]
当一个用户没有登录,如果要访问某些URL比如/admin,首先会跳转到/login这个URL,由于[ruls]中/login设置为anon,表示不需要进行身份认证。
当一个用户访问已经登录后,访问/admin这个URL就可以成功了。
当用户liy313登录成功后,访问/student时由于需要roles中teacher这个角色,而该用户并没有teacher角色,则就会调用[mian]中的roles.unauthorizedUrl=/unauthorized.jsp。访问/teacher则会成功,因为liy313有admin的角色,而admin角色有user:*的所有权限。
Url 匹配方式
? 匹配一个字符 /admin?可以匹配/admin1 /admin2但是不能匹配/admin12 /admin
* 匹配零个或者一个或者多个字符 /admin*可以匹配 /admin /admin1 /admin12但是不能匹配/admin/abc
** 匹配零个或者多个路径 /admin/**可以匹配/admin /admin/a /admin/a/b
4.servlet
创建两个servlet用户模拟
LoginServlet,模拟登录时访问的servlet
package com.liy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
public class LoginServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("login");
req.getRequestDispatcher("login.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
String userName = req.getParameter("userName");
String password = req.getParameter("password");
//由于web环境启动时已经帮我们实例化securityManager,并绑定到SecurityUtils中,直接获取subject
Subject currentUser = SecurityUtils.getSubject();
//创建令牌
UsernamePasswordToken token = new UsernamePasswordToken(userName,password);
try {
currentUser.login(token);
resp.sendRedirect("success.jsp");
} catch (Exception e) {
e.printStackTrace();
req.setAttribute("errInfo", "用户名或密码错误");
req.getRequestDispatcher("login.jsp").forward(req, resp);
}
}
}
AdminServlet,用户没有登录时访问会跳转或是登录后访问的servlet
package com.liy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
public class AdminServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("admin login get");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("admin login post");
}
}
并在web.xml文件中配置这两个servlet
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.liy.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>adminServlet</servlet-name>
<servlet-class>com.liy.servlet.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>adminServlet</servlet-name>
<url-pattern>/admin</url-pattern>
</servlet-mapping>
5.jsp页面
login.jsp模拟登录页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="login" method="post">
用户名:<input type="text" name="userName"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录">
${errInfo }
</form>
</body>
</html>
成功跳转页面success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>欢迎<shiro:principal/></h1>
</body>
</html>
角色或权限认证未通过的页面unauthorized.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
认证未通过
</body>
</html>