第二章:shiro集成web
官网帮助文档:https://shiro.apache.org/web.html
shiro集成web环境搭建
pom.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 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
| <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.2</version> </dependency>
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.2</version> </dependency>
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.21</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.21</version> <scope>compile</scope> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> <scope>provided</scope> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
|
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
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list>
<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> </web-app>
|
shiro.ini
初始化用户数据及角色数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [users]
kazu=123456,admin
jack=123456,teacher
tom=123456
[roles]
admin=user:*
teacher=student:*
|
拦截器
官方文档:https://shiro.apache.org/web.html#Web-defaultfilters
默认拦截器
案例1:测试匿名访问
创建LoginServlet
创建LoginServlet,完成doGet()请求,使用转发跳转到login登录页面
1 2 3 4 5 6 7 8 9 10
| @WebServlet("/login") public class LoginServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("==================doGet()======================"); req.getRequestDispatcher("/login.jsp").forward(req,resp); } }
|
shiro.ini
修改shiro配置文件,添加以下路径
案例2:测试登录
创建登录login.jsp页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户登录</title> </head> <body> <div> <font color="red">${errorInfo }</font> </div> <form action="login" method="post"> 用户名:<input type="text" name="userName"/><br/> 密码:<input type="password" name="password"/><br/> <input type="submit" value="登录"/> </form> </body> </html>
|
LoginServlet
修改LoginServlet类,重写doPost()方法
案例3:测试身份验证
需求
测试在浏览器中,未登录时访问/admin下所有的请求
创建AdminServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.bdqn.web;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@WebServlet("/admin") public class AdminServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("index.jsp"); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendRedirect("index.jsp"); } }
|
shiro.ini
修改shiro.ini配置文件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| [main]
authc.loginUrl=/login
[urls]
/login=anon
/admin=authc
|
案例4:验证角色
需求
访问/student学生资源,前提是访问学生资源必须拥有教师角色才能访问
shiro.ini
1 2 3 4 5 6 7 8 9
| [main]
roles.unauthorizedUrl=/unauthorize.jsp
[urls]
/student=roles[teacher]
|
测试
测试没有角色:
- 登录没有teacher角色的账户(如:kazu)
- 在浏览器中输入/student路径(结果:跳转到没有权限的页面)
测试拥有角色:
- 登录拥有teacher角色的账户(如:jack)
- 在浏览器中输入/student路径(结果:404请求)
案例5:验证权限
需求
访问/teacher教师资源,创建用户,前提是访问教师资源必须拥有管理员角色才能访问
shiro.ini
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
| [main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorize.jsp
perms.unauthorizedUrl=/unauthorize.jsp
[users]
kazu=123456,admin
jack=123456,teacher
tom=123456
[roles]
admin=user:*
teacher=student:*
[urls]
/login=anon
/admin=authc
/student=roles[teacher]
/teacher=perms["user:create"]
|
测试
测试没有权限:
- 登录没有创建用户权限的账户(如:jack)
- 在浏览器中输入/teacher路径(结果:跳转到没有权限的页面)
测试拥有权限:
- 登录拥有创建用户权限的账户(如:kazu)
- 在浏览器中输入/teacher路径(结果:404请求)
URL匹配方式
占位符 |
含义 |
说明 |
? |
匹配一个字符/admin? |
可以匹配/admin1或/admin2 不可以匹配/admin12 不可以匹配/admin |
* |
匹配0到n个字符/admin* |
可以匹配 /admin或/admin1或 /admin12 不可以匹配/admin/abc |
** |
匹配零个或者多个路径/admin/** |
可以匹配/admin 可以匹配/admin/a 可以匹配/admin/a/b 不可以匹配/adminxxx/a/b |
Shiro标签库
官方文档地址:https://shiro.apache.org/web.html#Web-taglibrary
导入shiro标签库
1
| <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
|
< shiro:guest >
游客(非登录用户)访问,无需登录即可查看
1 2 3
| <shiro:guest> 游客访问 <a href = "login.jsp">登录</a> </shiro:guest>
|
< shiro:principal >
principal 标签:显示用户身份信息,默认调用Subject.getPrincipal()获取,即Primary Principal
< shiro:user >
user 标签:用户已经通过认证\记住我 登录后显示响应的内容
1 2 3
| <shiro:user> 欢迎[<shiro:principal/>]登录 <a href = "logout">退出</a> </shiro:user>
|
< shiro:hasRole >
判断是否拥有某个角色
1 2 3
| <shiro:hasRole name="admin"> 欢迎有admin角色的用户! </shiro:hasRole>
|
< shiro:hasPermission >
判断是否拥有某个权限
1 2 3
| <shiro:hasPermission name="student:create"> 欢迎有student:create权限的用户! </shiro:hasPermission>
|
< shiro:authenticated >
authenticated标签:用户身份验证通过,即 Subject.login() 登录成功 不是记住我登录的
1 2 3
| <shiro:authenticated> 用户[<shiro:principal/>] 已身份验证通过 </shiro:authenticated>
|
< shiro:notAuthenticated >
notAuthenticated标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括”记住我”也属于未进行身份验证
1 2 3
| <shiro:notAuthenticated> 未身份验证(包括"记住我") </shiro:notAuthenticated>
|
加密
散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,可以到一些md5 解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。
加密测试
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
| package com.bdqn.test;
import org.apache.shiro.crypto.hash.Md5Hash;
import java.util.UUID;
public class PasswordTest { public static void main(String[] args) { String pwd = "123456"; Md5Hash md5Hash1 = new Md5Hash(pwd); System.out.println("加密后的密码:"+md5Hash1); Md5Hash md5Hash2 = new Md5Hash(pwd, UUID.randomUUID().toString()); Md5Hash md5Hash3 = new Md5Hash(pwd, "bdqn"); System.out.println("加密后的密码:"+md5Hash2); System.out.println("加密后的密码:"+md5Hash3); Md5Hash md5Hash4 = new Md5Hash(pwd, "bdqn",2); System.out.println("加密后的密码:"+md5Hash4); } }
|
实际开发中,建议使用加入盐值及散列次数进行密码加密操作
加密工具类
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
| package com.bdqn.utils;
import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.Sha1Hash;
public class PasswordUtil {
public static String md5(String source, Object salt, Integer hashIterations) { return new Md5Hash(source, salt, hashIterations).toString(); }
public static String sha1(String source, Object salt, Integer hashIterations) { return new Sha1Hash(source, salt, hashIterations).toString(); } }
|