本章学习目标 小风 Java 实战系列教程 Shiro 核心功能 Shiro 的 Web 集成 Spring 与 Shiro 整合 SpringBoot 整合 Shiro 1. Shiro 核心功能 1.1. RBAC 模型 在 RBAC 的模型, 涉及到三个关键的元素 : 1) 用户 : 系统的使

Similar documents
"+handlermethod.getbean().getclass().getname()); public void aftercompletion(httpservletrequest req, HttpServletResponse resp, Object handler, Excepti

将 MD5 的工具类拷贝到项目中 二 微服务模块的搭建 我们将权限的查询放到一个单独的模块中, 这个模块提供接口供给消费者远程调用 (RPC), 这次范例是微服开发的雏形, 在以后你使用 springcloud 的时候会使用到今天的概念 1 使用 maven 创建新的模块 (microboot-sh

1.2. Sql 映射配置 小风 Java 实战系列教程 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//dtd Mapper 3.0//EN" "

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

2. AOP 底层技术实现 小风 Java 实战系列教程 关键词 : 代理模式 代理模型分为两种 : 1) 接口代理 (JDK 动态代理 ) 2) 子类代理 (Cglib 子类代理 ) 需求 :CustomerService 业务类, 有 save,update 方法, 希望在 save,updat

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

新・解きながら学ぶJava

EJB-Programming-4-cn.doc

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

D getinitparameternames() 9 下 列 选 项 中, 属 于 Servlet API 中 提 供 的 request 对 象 的 包 装 类 的 是 ( ) A HttpServletRequestWrapper B HttpServletRequest C HttpServ

untitled

<!-- import outer proper

基于CDIO一体化理念的课程教学大纲设计

IoC容器和Dependency Injection模式.doc

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

Servlet

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

在Spring中使用Kafka:Producer篇

untitled

Microsoft Word - 01.DOC

FileMaker 16 ODBC 和 JDBC 指南

使用 XFire 与 Spring 开发 Web Service 2 实现功能与特点 基于 J2EE 平台的 Web Service 服务 开发方便, 配置简单 设计接口 实现服务 配置暴露接口 XFire 将自动生成对应的 wsdl 支持高级详细配置 与 Spring 无缝集成 运行环境 JDK

输入 project name 选择完成

没 有 多 余 的 Contruol 或 Action 了 原 来 Domain 层 被 服 务 层 Service layer 遮 挡, 在 右 边 图 中, 则 Domain 层 直 接 暴 露 给 前 台 了, 没 有 被 遮 挡, 裸 露 了 这 样 一 步 到 位 实 现 领 域 模 型

PrintWriter s = new PrintWriter(writer); ex.printstacktrace(s); mv.addobject("exception", writer.tostring()); mv.setviewname("error"); return

new 进行创建对象, 是程序主动去创建依赖对象 ; 而 IoC 是有专门一个容器来创建这些对象, 即由 Ioc 容器来控制对象的创建 ; 谁控制谁? 当然是 IoC 容器控制了对象 ; 控制什么? 那就是主要控制了外部资源获取 ( 不只是对象包括比如文件等 ) 为何是反转, 哪些方面反转了 : 有

5-1 nav css 5-2

北 风 网 讲 师 原 创 作 品 ---- 仅 供 学 员 内 部 交 流 使 用 前 言 吾 尝 终 日 而 思 矣, 不 如 须 臾 之 所 学 也 ; 吾 尝 跂 而 望 矣, 不 如 登 高 之 博 见 也 登 高 而 招, 臂 非 加 长 也, 而 见

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

Flume-ng与Mysql整合开发

RUN_PC連載_10_.doc

RunPC2_.doc

获取 Access Token access_token 是接口的全局唯一票据, 接入方调用各接口时都需使用 access_token 开发者需要进行妥善保存 access_token 的存储至少要保留 512 个字符空间 access_token 的有效期目前为 2 个小时, 需定时刷新, 重复

ASP.NET MVC Visual Studio MVC MVC 範例 1-1 建立第一個 MVC 專案 Visual Studio MVC step 01 Visual Studio Web ASP.NET Web (.NET Framework) step 02 C:\M

优迈科技教学大纲2009版本

XXXXXXXX

resp.getwriter().print(j + "*" + i + "=" + j * i+" "); resp.getwriter().print("<br/>"); protected void dopost(httpservletrequest req, HttpServletRespo

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

Microsoft Word - 第3章.doc

内 容 简 介 本 书 是 一 本 关 于 语 言 程 序 设 计 的 教 材, 涵 盖 了 语 言 的 基 本 语 法 和 编 程 技 术, 其 中 包 含 了 作 者 对 语 言 多 年 开 发 经 验 的 总 结, 目 的 是 让 初 学 的 读 者 感 受 到 语 言 的 魅 力, 并 掌

OSWorkflow Documentation

《大话设计模式》第一章

使用MapReduce读取XML文件

Java

jsp

untitled

FileMaker 15 ODBC 和 JDBC 指南

服务框架 HSF 使用与配置 一江更新时间 : 目录 HSF 常用 OPS 和开发工具介绍... 1 HSF 的使用和配置... 2 下载和安装 HSF... 2 服务开发与部署... 5 服务查询 服务调用 HSFUNIT 测试包的使用 HSF

1

JavaIO.PDF

雲端 Cloud Computing 技術指南 運算 應用 平台與架構 10/04/15 11:55:46 INFO 10/04/15 11:55:53 INFO 10/04/15 11:55:56 INFO 10/04/15 11:56:05 INFO 10/04/15 11:56:07 INFO


TopTest_Adminstrator.doc

chp6.ppt

Microsoft Word - PHP7Ch01.docx

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

untitled

(CIP) Web /,. :, ISBN X.W T P393.4 CIP (2004) Web ( ) ( / ) : * 787

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile..

JAR 包 : doget 方法 : 1. import java.io.ioexception; 2. import java.io.printwriter; 3. import javax.servlet.servletexception; 4. import javax.servlet.htt

中 国 矿 业 大 学

untitled

Java 1 Java String Date

Microsoft PowerPoint - ch6 [相容模式]

Swing-02.pdf

Transcription:

本章学习目标 Shiro 核心功能 Shiro 的 Web 集成 Spring 与 Shiro 整合 SpringBoot 整合 Shiro 1. Shiro 核心功能 1.1. RBAC 模型 在 RBAC 的模型, 涉及到三个关键的元素 : 1) 用户 : 系统的使用用户 ( 登录用户 ) 2) 角色 : 拥有相同的权限的用户 3) 权限 : 系统可以被用户操作的元素 ( 例如 : 系统菜单, 超链接, 文件等 ) 以上三个元素有一定的关系 : 1) 用户和角色是多对多的关系 2) 角色和权限是多对多的关系 1.2. 设计权限表结构 使用 PowerDesigner

1.3. Shiro 框架简介 Shiro 是 Apache 组织的开源的 Java 安全框架 Shiro 的 6 个核心功能 :

其中, 认证与授权是 Shiro 的基础核心功能 1.4. Shiro 的三大核心 API

1)Subject 关联 SecurityManager 2)SecurityManager 关联 Realm 1.5. Shiro 的认证操作 1.5.1. 建立 maven 项目, 导入 shiro 的坐标 <!-- 导入 shiro 的坐标 --> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-all</artifactid> <version>1.3.2</version> </dependency>

注意 :Shiro 底层依赖 commons-logging 包, 如果不导入的话 : <dependency> <groupid>commons-logging</groupid> <artifactid>commons-logging</artifactid> <version>1.2</version> </dependency> 1.5.2. 建立自定义 Realm package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception; import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; /** * 自定义 Realm * @author lenovo * */ public class MyRealm extends AuthorizingRealm{

// 授权方法 : 获取授权信息 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { return null; // 认证方法 : 获取认证信息 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { return null; 1.5.3. 建立 ini 配置 SecurityManager 关联 Realm 建立 shiro.ini 配置, 内容如下 : myrealm=cn.sm1234.realms.myrealm securitymanager.realm=$myrealm 1.5.4. 编写 Shiro 的认证流程 package cn.sm1234.shiro; import org.apache.shiro.securityutils;

import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.incorrectcredentialsexception; import org.apache.shiro.authc.unknownaccountexception; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.config.inisecuritymanagerfactory; import org.apache.shiro.mgt.securitymanager; import org.apache.shiro.subject.subject; import org.apache.shiro.util.factory; /** * 执行 Shiro 的认证流程 * @author lenovo * */ public class Demo1 { public static void main(string[] args) { //1. 创建安全管理器工厂 Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2. 创建安全管理器 SecurityManager securitymanager = factory.getinstance(); //3. 初始化 SecurityUtils 工具类 SecurityUtils.setSecurityManager(securityManager); //4. 从 SecurityUtils 工具中获取 Subject Subject subject = SecurityUtils.getSubject();

//5. 认证操作 ( 登录 ) //AuthenticationToken: 用于封装用户输入的账户信息 AuthenticationToken token = new UsernamePasswordToken("eric", "123456"); try { subject.login(token); // 如果 login 方法没有任何异常, 代表认证成功 System.out.println(" 登录成功 "); catch (UnknownAccountException e) { // 账户不存在 System.out.println(" 账户不存在 "); catch (IncorrectCredentialsException e) { // 密码错误 System.out.println(" 密码错误 "); catch (Exception e) { // 系统错误 System.out.println(" 系统错误 "); 1.5.5. 在 Realm 编写认证的逻辑 package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception;

import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.simpleauthenticationinfo; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; /** * 自定义 Realm * @author lenovo * */ public class MyRealm extends AuthorizingRealm{ // 授权方法 : 获取授权信息 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { return null; // 认证方法 : 获取认证信息 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { System.out.println(" 执行认证方法..."); // 判断用户名是否存在, 判断密码是否正确

//1. 如果获取用户输入的账户信息? UsernamePasswordToken token = (UsernamePasswordToken)arg0; String username = token.getusername(); //2. 如果获取数据库的账户信息? // 模拟数据库的账户信息 String name = "jack"; String password = "1234"; // 判断用户名 if(!username.equals(name)){ return null; // shiro 底层自动抛出 UnknownAccountException // 判断密码 /** * 参数一 :principal, 用于把数据回传到 login 方法 * 参数二 : 数据库的密码 * Shiro 底层对比密码的结果 : * 1) 密码正确 : 认证通过 * 2) 密码不正确 : 自动抛出 IncorrectCredentialsException * 参数三 :realm 的名称, 只有在多个 realm 的是才会使用 */ return new SimpleAuthenticationInfo("callback",password,"");

1.6. Shiro 的授权操作 Shiro 的授权操作分为两种不同方式 : 1) 基于资源的授权必须得到资源的授权码才可以访问该资源 2) 基于角色的授权必须得到该角色, 才可以访问该资源 注意 : 如果要进行 Shiro 的授权操作, 必须先完成 Shiro 的认证 1.6.1. 基于资源的授权 // 进行 Shiro 的授权 //1. 基于资源的授权 // 判断当前登录用户是否有 商品添加 功能 //ispermitted(): 返回 true, 有权限, false: 没有权限 System.out.println("productAdd="+subject.isPermitted("productAdd")); Realm 的授权方法 : // 授权方法 : 获取授权信息 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { System.out.println(" 执行授权方法..."); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 资源授权码 info.addstringpermission("productadd");

return info; 1.6.2. 基于角色的授权 //2. 基于角色的授权 // 判断当前登录用户是否为 超级管理员 System.out.println("admin="+subject.hasRole("admin")); // 授权方法 : 获取授权信息 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { System.out.println(" 执行授权方法..."); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 资源授权码 //info.addstringpermission("productadd"); // 进行通配符授权 info.addstringpermission("product:*"); // 角色授权码 info.addrole("admin"); return info;

2. Shiro 的 Web 集成 2.1. Shiro 与 Web 集成环境搭建 2.1.1. 建立 maven 的 web 项目, 导入坐标 <!-- shiro 坐标 --> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-all</artifactid> <version>1.3.2</version> </dependency> <dependency> <groupid>commons-logging</groupid> <artifactid>commons-logging</artifactid> <version>1.2</version> </dependency> <!-- servlet,jsp 坐标 --> <dependency> <groupid>javax.servlet</groupid>

<artifactid>javax.servlet-api</artifactid> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupid>javax.servlet</groupid> <artifactid>jsp-api</artifactid> <version>2.0</version> <scope>provided</scope> </dependency> 2.1.2. 配置 web.xml(*) <!-- shiro 的 web 环境的初始化 : 监听器 --> <!-- EnvironmentLoaderListener: 在 web 应用中加载 shiro 的环境, 加载 shiro.ini --> <listener> <listener-class>org.apache.shiro.web.env.environmentloaderlistener</listener-cl ass> </listener> <!-- 默认加载 WEB-INF/shiro.ini 文件, 如果需要修改路径 --> <context-param> <param-name>shiroconfiglocations</param-name> <param-value>classpath:shiro.ini</param-value> </context-param> <!-- shiro 的过滤器 --> <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> 2.1.3. 定义 Realm package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception; import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; public class MyRealm extends AuthorizingRealm{ @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { System.out.println(" 执行了授权方法 "); return null; @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException {

System.out.println(" 执行了认证方法 "); return null; 2.1.4. 配置 shiro.ini [main] myrealm=cn.sm1234.realms.myrealm securitymanager.realm=$myrealm 2.2. Shiro 的 web 内置过滤器 (*) shiro 的 web 内置过滤器,shiro 自己提供一些专门用于认证, 授权的过滤器 shiro 分为两种过滤器 : 1) 认证过滤器 : anon: 用户不需要认证也可以访问 authc: 用户必须认证才可以访问 user: 用户只要 rememberme, 就可以访问 2) 授权过滤器 perms: 基于资源的授权过滤器 roles : 基于角色的授权过滤器 例如 : [main]

myrealm=cn.sm1234.realms.myrealm securitymanager.realm=$myrealm [urls] /index.jsp=authc 2.3. 编写 Shiro 的认证操作 2.3.1. 登录页面 <%@ 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> 登录页面 </title> </head> <body> <h3> 用户登录 </h3> <form method="post" action="${pagecontext.request.contextpath/login"> 用户名 :<input type="text" name="name"/><br/> 密码 :<input type="password" name="password"/><br/> <input type="submit" value=" 登录 "> </form> </body> </html>

2.3.2. 编写 LoginServlet package cn.sm1234.web; 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.authenticationtoken; import org.apache.shiro.authc.incorrectcredentialsexception; import org.apache.shiro.authc.unknownaccountexception; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.subject.subject; /** * 登录 Servlet */ public class LoginServlet extends HttpServlet { private static final long serialversionuid = 1L; protected void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { //1. 设置请求编码 request.setcharacterencoding("utf-8");

//2. 接收用户名和密码 String name = request.getparameter("name"); String password = request.getparameter("password"); //3. 调用 login 方法 //3.1 获取 Subject Subject subject = SecurityUtils.getSubject(); AuthenticationToken token = new UsernamePasswordToken(name, password); try { subject.login(token); // 获取 Principal String dbname = (String)subject.getPrincipal(); // 把用户信息存储到 session request.getsession().setattribute("username", name); // 登录成功 response.sendredirect(request.getcontextpath()+"/index.jsp"); catch (UnknownAccountException e) { request.setattribute("msg", " 用户名不存在 "); request.getrequestdispatcher("/login.jsp").forward(request, response); catch (IncorrectCredentialsException e) { request.setattribute("msg", " 密码错误 "); request.getrequestdispatcher("/login.jsp").forward(request, response);

protected void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { doget(request, response); 2.3.3. 编写 Realm 的认证方法 package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception; import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.simpleauthenticationinfo; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; public class MyRealm extends AuthorizingRealm{ @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { System.out.println(" 执行了授权方法 "); return null;

@Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { System.out.println(" 执行了认证方法 "); //1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken)arg0; // 模拟数据库的密码 String name = "jack"; String password = "1234"; if(!token.getusername().equals(name)){ // 用户不存在 return null; // 返回密码 return new SimpleAuthenticationInfo(name,password,""); 注意 :login 登录请求必须使用 anon 放行 : [main] myrealm=cn.sm1234.realms.myrealm

securitymanager.realm=$myrealm [urls] /product/list.jsp=anon /login=anon /**=authc 2.4. 编写 Shiro 的授权操作 2.4.1. 使用 Shiro 内置授权过滤器 [main] myrealm=cn.sm1234.realms.myrealm securitymanager.realm=$myrealm [urls] /product/list.jsp=anon /login=anon /product/add.jsp=perms[product:add] /**=authc 2.4.2. 配置未授权提示页面 [main] perms.unauthorizedurl=/unauth.jsp myrealm=cn.sm1234.realms.myrealm securitymanager.realm=$myrealm [urls]

/product/list.jsp=anon /login=anon /product/add.jsp=perms[product:add] /**=authc 2.4.3. 编写 Realm 的授权逻辑 package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception; import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.simpleauthenticationinfo; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.authz.simpleauthorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; public class MyRealm extends AuthorizingRealm{ @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { System.out.println(" 执行了授权方法 "); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addstringpermission("product:add");

return info; @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { System.out.println(" 执行了认证方法 "); //1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken)arg0; // 模拟数据库的密码 String name = "jack"; String password = "1234"; if(!token.getusername().equals(name)){ // 用户不存在 return null; // 返回密码 return new SimpleAuthenticationInfo(name,password,"");

2.5. 分析 Shiro 内置过滤器的原理 authc:org.apache.shiro.web.filter.authc.formauthenticationfilter anon:org.apache.shiro.web.filter.authc.anonymousfilter perms:org.apache.shiro.web.filter.authz.permissionsauthorizationfilter roles:org.apache.shiro.web.filter.authz.rolesauthorizationfilter 3. Spring 整合 Shiro(SSM 整合 ) 3.1. 搭建项目环境 ( 导入 SpringMVC) 3.1.1. 建立 maven 项目, 导入 Shiro 的坐标 <dependencies> <!-- shiro 坐标 --> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-all</artifactid> <version>1.3.2</version> </dependency>

<dependency> <groupid>commons-logging</groupid> <artifactid>commons-logging</artifactid> <version>1.2</version> </dependency> <!-- servlet,jsp 坐标 --> <dependency> <groupid>javax.servlet</groupid> <artifactid>javax.servlet-api</artifactid> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupid>javax.servlet</groupid> <artifactid>jsp-api</artifactid> <version>2.0</version> <scope>provided</scope> </dependency> <!-- 导入 SpringMVC 支持 --> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-webmvc</artifactid> <version>4.3.3.release</version> </dependency> </dependencies>

3.1.2. 配置 web.xml, 启动 SpringMVC <!-- 启动 SpringMVC --> <servlet> <servlet-name>dispatcherservlet</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class > <load-on-startup>1</load-on-startup> <init-param> <param-name>contextconfiglocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherservlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> 3.1.3. 配置 spring-mvc.xml <?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:contenxt="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc

http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Controller 类扫描包 --> <contenxt:component-scan base-package="cn.sm1234.controller"/> <!-- 注解驱动 --> <mvc:annotation-driven></mvc:annotation-driven> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.internalresourceviewresolver"> <!-- 前缀 --> <property name="prefix" value="/web-inf/jsp/"/> <!-- 后缀 --> <property name="subffix" value=".jsp"></property> </bean> </beans> 然后拷贝 shiro-web 项目的 jsp 页面到当前项目 :

3.1.4. 编写 Controller 类 package cn.sm1234.controller; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; /** * 主控制器 * @author lenovo * */ @Controller @RequestMapping("/") public class MainController { @RequestMapping("/index")

public String index(){ return "index"; @RequestMapping("/toLogin") public String tologin(){ return "login"; @RequestMapping("/unAuth") public String unauth(){ return "unauth"; package cn.sm1234.controller; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; @Controller @RequestMapping("/product") public class ProductController { @RequestMapping("/toAdd") public String toadd(){ return "product/add";

@RequestMapping("/toList") public String tolist(){ return "product/list"; @RequestMapping("/toUpdate") public String toupdate(){ return "product/update"; 3.2. Spring 整合 Shiro 3.2.1. 导入 Spring 的坐标 <!-- 导入 Spring 的坐标 --> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-web</artifactid> <version>4.3.3.release</version> </dependency> 3.2.2. 配置 web.xml (*) <!-- 配置 Spring 整合 Shiro 的过滤器 --> <!-- DelegatingFilterProxy: 这个类的作用是把请求拦截下来, 把请求交给 Spirng 容器的一个 bean

处理 (bean 的 id 就是 filter-name 的名称 ) --> <filter> <filter-name>shirofilter</filter-name> <filter-class>org.springframework.web.filter.delegatingfilterproxy</filter-clas s> </filter> <filter-mapping> <filter-name>shirofilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 启动 Spring 环境 --> <listener> <listener-class>org.springframework.web.context.contextloaderlistener</listener -class> </listener> <context-param> <param-name>contextconfiglocation</param-name> <param-value>classpath:applicationcontext.xml</param-value> </context-param> 3.2.3. 编写 applicationcontext.xml(*) <?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"

xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Shiro 与 Spring 整合 --> <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean"> <!-- 关联 SecurityManager --> <property name="securitymanager" ref="securitymanager"/> </bean> <bean id="securitymanager" class="org.apache.shiro.web.mgt.defaultwebsecuritymanager"> <!-- 关联 realm --> <property name="realm" ref="realm"/> </bean> <bean id="realm" class="cn.sm1234.realms.myrealm"/> </beans> 3.2.4. 编写 Realm package cn.sm1234.realms; import org.apache.shiro.authc.authenticationexception; import org.apache.shiro.authc.authenticationinfo; import org.apache.shiro.authc.authenticationtoken;

import org.apache.shiro.authz.authorizationinfo; import org.apache.shiro.realm.authorizingrealm; import org.apache.shiro.subject.principalcollection; public class MyRealm extends AuthorizingRealm{ @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { // TODO Auto-generated method stub return null; @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // TODO Auto-generated method stub return null; 3.3. 使用 Shiro 的认证过滤器拦截页面 <!-- Shiro 与 Spring 整合 --> <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean"> <!-- 关联 SecurityManager --> <property name="securitymanager" ref="securitymanager"/>

<!-- 使用 Shiro 的内置过滤器拦截资源 --> <property name="filterchaindefinitions"> <value> /product/tolist=anon /**=authc </value> </property> </bean> <!-- 修改 shiro 的默认登录请求 --> <property name="loginurl" value="/tologin"/> 3.4. 编写 Shiro 的认证操作 3.4.1. 登录页面 <h3> 用户登录 </h3> <font color="red">${msg</font> <form method="post" action="${pagecontext.request.contextpath/user/login"> 用户名 :<input type="text" name="name"/><br/> 密码 :<input type="password" name="password"/><br/> <input type="submit" value=" 登录 "> </form> 3.4.2. 编写 Controller package cn.sm1234.controller; import javax.servlet.http.httpservletrequest;

import org.apache.shiro.securityutils; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.incorrectcredentialsexception; import org.apache.shiro.authc.unknownaccountexception; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.subject.subject; import org.springframework.stereotype.controller; import org.springframework.ui.model; import org.springframework.web.bind.annotation.requestmapping; import cn.sm1234.domain.user; @Controller @RequestMapping("/user") public class UserController { /** * 登录 */ @RequestMapping("/login") public String login(user user,httpservletrequest request,model model){ // 使用 Shiro 的认证操作 //1. 获取 Subject 对象 Subject subject = SecurityUtils.getSubject(); //2. 封装用户信息 AuthenticationToken token = new UsernamePasswordToken(user.getName(),

user.getpassword()); //3. 执行认证 try { subject.login(token); String username = (String)subject.getPrincipal(); request.getsession().setattribute("username", username); // 登录成功 return "redirect:/index"; catch (UnknownAccountException e) { model.addattribute("msg", " 用户不存在 "); catch (IncorrectCredentialsException e) { model.addattribute("msg", " 密码输入有误 "); return "login"; 3.4.3. 编写 Realm 的认证操作 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // 1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken) arg0;

// 模拟数据库的密码 String name = "jack"; String password = "1234"; if (!token.getusername().equals(name)) { // 用户不存在 return null; // 返回密码 return new SimpleAuthenticationInfo(name, password, ""); @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // 1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken) arg0; // 模拟数据库的密码 String name = "jack"; String password = "1234"; if (!token.getusername().equals(name)) { // 用户不存在 return null; // 返回密码

return new SimpleAuthenticationInfo(name, password, ""); 注意 : 必须放行登录请求 : <!-- 使用 Shiro 的内置过滤器拦截资源 --> <property name="filterchaindefinitions"> <value> /product/tolist=anon /user/login=anon /**=authc </value> </property> 3.5. 编写 Shiro 的授权操作 3.5.1. 使用 Shiro 授权过滤器拦截资源 <!-- Shiro 与 Spring 整合 --> <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean"> <!-- 关联 SecurityManager --> <property name="securitymanager" ref="securitymanager"/> <!-- 使用 Shiro 的内置过滤器拦截资源 --> <property name="filterchaindefinitions"> <value> /product/tolist=anon /user/login=anon /product/toadd=perms[product:add] /**=authc </value>

</property> </bean> <!-- 修改 shiro 的默认登录请求 --> <property name="loginurl" value="/tologin"/> 3.5.2. 配置未授权提示页面 <!-- Shiro 与 Spring 整合 --> <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean"> <!-- 关联 SecurityManager --> <property name="securitymanager" ref="securitymanager"/> <!-- 使用 Shiro 的内置过滤器拦截资源 --> <property name="filterchaindefinitions"> <value> /product/tolist=anon /user/login=anon /product/toadd=perms[product:add] /**=authc </value> </property> <!-- 修改 shiro 的默认登录请求 --> <property name="loginurl" value="/tologin"/> <!-- 添加未授权提示页面 --> <property name="unauthorizedurl" value="/unauth"/> </bean>

3.5.3. 编写 Realm 的授权代码 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 基于资源的授权 info.addstringpermission("product:add"); return info; 3.6. 整合 MyBatis 3.6.1. 导入相关 jar 包 <!-- mybatis 的坐标 --> <dependency> <groupid>org.mybatis</groupid> <artifactid>mybatis</artifactid> <version>3.4.4</version> </dependency> <!-- mybatis 与 spring 整合 --> <dependency> <groupid>org.mybatis</groupid> <artifactid>mybatis-spring</artifactid> <version>1.3.0</version> </dependency> <!-- 连接池 --> <dependency>

<groupid>com.alibaba</groupid> <artifactid>druid</artifactid> <version>1.1.7</version> </dependency> <!-- mysql 驱动程序 --> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version>5.1.41</version> </dependency> <!-- Spring 持久层支持 --> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-jdbc</artifactid> <version>4.3.3.release</version> </dependency> 3.6.2. jdbc.properties jdbc.url = jdbc:mysql://localhost:3306/shiro jdbc.driverclassname = com.mysql.jdbc.driver jdbc.user = root jdbc.password = root 3.6.3. 编写 applicationcontext.xml 配置 <?xml version="1.0" encoding="utf-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 加载 applicationcontext-shiro.xml --> <import resource="classpath:applicationcontext-shiro.xml"/> <!-- 加载 mybatis 与 Spring 相关配置 --> <!-- 加载 jdbc.properties 配置 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 创建数据源 --> <bean id="datasource" class="com.alibaba.druid.pool.druiddatasource"> <property name="url" value="${jdbc.url"/> <property name="driverclassname" value="${jdbc.driverclassname"/> <property name="username" value="${jdbc.user"/> <property name="password" value="${jdbc.password"/> <property name="maxactive" value="10"/> </bean>

<!-- mybatis 与 spring 整合 --> <bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean"> <property name="datasource" ref="datasource"/> <!-- mybatis 别名包扫描 --> <property name="typealiasespackage" value="cn.sm1234.domain"/> </bean> <!-- mybatis 的 Mapper 接口的扫描 --> <bean class="org.mybatis.spring.mapper.mapperscannerconfigurer"> <property name="basepackage" value="cn.sm1234.dao"/> </bean> <!-- 开启 jdbc 事务 --> <bean id="transactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager"> <property name="datasource" ref="datasource"/> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionmanager"/> </beans> <context:component-scan base-package="cn.sm1234"/> 3.6.4. 编写 Mapper 接口 package cn.sm1234.dao; import cn.sm1234.domain.user;

public interface UserMapper { public User findbyname(string name); 3.6.5. 编写 Mapper 映射文件 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//dtd Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.sm1234.dao.usermapper"> <select id="findbyname" parametertype="string" resulttype="user"> select id, name, password from shiro.t_user where name = #{value </select> </mapper> 3.6.6. 编写测试类 package cn.sm1234.test;

import javax.annotation.resource; import org.junit.test; import org.junit.runner.runwith; import org.springframework.test.context.contextconfiguration; import org.springframework.test.context.junit4.springjunit4classrunner; import cn.sm1234.dao.usermapper; import cn.sm1234.domain.user; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class UserMapperTest { @Resource private UserMapper usermapper; @Test public void testfindbyname(){ User user = usermapper.findbyname("eric"); System.out.println(user);

3.7. 编写动态认证和授权代码 3.7.1. 动态认证代码 3.7.1.1. Mapper 接口 public interface UserMapper { public User findbyname(string name); 3.7.1.2. Service 接口和实现 接口 : package cn.sm1234.service; import cn.sm1234.domain.user; public interface UserService { public User findbyname(string name); 实现 : package cn.sm1234.service.impl; import javax.annotation.resource; import org.springframework.stereotype.service;

import org.springframework.transaction.annotation.transactional; import cn.sm1234.dao.usermapper; import cn.sm1234.domain.user; import cn.sm1234.service.userservice; @Service @Transactional public class UserServiceImpl implements UserService { @Resource private UserMapper usermapper; @Override public User findbyname(string name) { return usermapper.findbyname(name); 3.7.1.3. MyReal // 认证方法 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // 1. 获取用户输入的账户信息

UsernamePasswordToken token = (UsernamePasswordToken) arg0; /* // 模拟数据库的密码 String name = "jack"; String password = "1234"; if (!token.getusername().equals(name)) { // 用户不存在 return null; */ // 实现动态认证 User dbuser = userservice.findbyname(token.getusername()); if(dbuser==null){ // 用户不存在 return null; // 返回密码 return new SimpleAuthenticationInfo(dbUser, dbuser.getpassword(), ""); 3.7.2. 动态授权代码 模拟查询用户拥有的权限码 SQL 语句 : -- 查询某个用户目前拥有的权限 SELECT p.permission FROM t_user u

INNER JOIN t_user_role ur ON u.id = ur.user_id INNER JOIN t_role_permission rp ON ur.role_id = rp.role_id INNER JOIN t_permission p ON rp.permission_id = p.id WHERE u.id = 2; 3.7.2.1. UserMapper 接口 package cn.sm1234.dao; import java.util.list; import cn.sm1234.domain.user; public interface UserMapper { /** * 根据用户名查询用户 * @param name * @return */ public User findbyname(string name); /** * 根据用户 ID 查询用户拥有的资源授权码 */ public List<String> findpermissionbyuserid(integer userid);

3.7.2.2. Mapper 映射文件 <select id="findpermissionbyuserid" parametertype="int" resulttype="string"> SELECT p.permission FROM t_user u INNER JOIN t_user_role ur ON u.id = ur.user_id INNER JOIN t_role_permission rp ON ur.role_id = rp.role_id INNER JOIN t_permission p ON rp.permission_id = p.id WHERE u.id = #{value; </select> 3.7.2.3. Service 接口和实现 接口 : public List<String> findpermissionbyuserid(integer userid); 实现 : @Override public List<String> findpermissionbyuserid(integer userid) { return usermapper.findpermissionbyuserid(userid); 3.7.2.4. MyRealm // 授权方法 @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection arg0) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

/*// 基于资源的授权 info.addstringpermission("product:add"); // 基于角色的授权 info.addrole("admin");*/ // 给当前登录用户进行动态授权 //1. 获取当前用户的 pricipal Subject subject = SecurityUtils.getSubject(); User dbuser = (User)subject.getPrincipal(); //2. 查询当前用户拥有的资源授权码 List<String> perms = userservice.findpermissionbyuserid(dbuser.getid()); if(perms!=null){ // 遍历授权 for (String perm : perms) { if(!stringutils.isempty(perm)){ info.addstringpermission(perm); return info; 注意此时的 shiro 过滤器配置 : <!-- 使用 Shiro 的内置过滤器拦截资源 --> <property name="filterchaindefinitions">

<value> /user/login=anon /product/tolist=perms[product:list] /product/toadd=perms[product:add] /product/toupdate=perms[product:update] /**=authc </value> </property> 3.8. 使用 Shiro 的 JSP 权限标签 3.8.1. 在 JSP 页面导入 shiro 标签库 <%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%> 3.8.2. 使用 Shiro 权限标签 <shiro:haspermission name="product:add"> <a href="${pagecontext.request.contextpath/product/toadd"> 商品添加 </a><br/> </shiro:haspermission> <shiro:haspermission name="product:update"> <a href="${pagecontext.request.contextpath/product/toupdate"> 商品修改 </a><br/> </shiro:haspermission> <shiro:haspermission name="product:list"> <a href="${pagecontext.request.contextpath/product/tolist"> 商品列表 </a><br/> </shiro:haspermission>

4. Spring Boot 整合 Shiro( 整合 SSM) 4.1. 搭建 Spring Boot 项目环境 4.1.1. 建立 maven 项目, 导入坐标 <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> <!-- Spring Boot 父工程 --> <parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>1.5.4.release</version> </parent> <groupid>cn.sm1234</groupid> <artifactid>shiro-springboot</artifactid> <version>0.0.1-snapshot</version>

<dependencies> <!-- web 支持,SpringMVC, Servlet 支持等 --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <!-- 导入 thymeleaf 支持 --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> </dependencies> <!-- 属性 --> <properties> <!-- JDK 编译版本 --> <java.version>1.8</java.version> <!-- 把 thymeleaf 升级为 3.0 以上 --> <thymeleaf.version>3.0.2.release</thymeleaf.version> <thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version> </properties> </project> 4.1.2. 编写 Controller package cn.sm1234.controller; import org.springframework.stereotype.controller;

import org.springframework.web.bind.annotation.requestmapping; /** * 主控制器 * @author lenovo * */ @Controller @RequestMapping("/") public class MainController { @RequestMapping("/index") public String index(){ return "index"; @RequestMapping("/toLogin") public String tologin(){ return "login"; @RequestMapping("/unAuth") public String unauth(){ return "unauth"; package cn.sm1234.controller;

import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; @Controller @RequestMapping("/product") public class ProductController { @RequestMapping("/toAdd") public String toadd(){ return "product/add"; @RequestMapping("/toList") public String tolist(){ return "product/list"; @RequestMapping("/toUpdate") public String toupdate(){ return "product/update";

4.1.3. 编写 html 页面 4.2. Shiro 整合 4.2.1. 导入 shiro 整合坐标 <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-spring</artifactid> <version>1.4.0</version> </dependency> 4.2.2. 编写 Shiro 配置类 package cn.sm1234.shiro; import org.apache.shiro.spring.web.shirofilterfactorybean; import org.apache.shiro.web.mgt.defaultwebsecuritymanager; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration;

/** * Shiro 的配置类 * @author lenovo * */ @Configuration public class ShiroConfig { /** * 1. 创建 ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shirofilterfactorybean(defaultwebsecuritymanager securitymanager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 关联 SecurityManager bean.setsecuritymanager(securitymanager); return bean; /** * 2. 创建 SecurityManager */ @Bean public DefaultWebSecurityManager defaultwebsecuritymanager(myrealm realm){ DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

// 关联 realm manager.setrealm(realm); return manager; /** * 3. 创建 Realm */ @Bean public MyRealm myreal(){ MyRealm realm = new MyRealm(); return realm; 4.3. 配置 Shiro 认证过滤器实现资源拦截 /** * 1. 创建 ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shirofilterfactorybean(defaultwebsecuritymanager securitymanager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 关联 SecurityManager

bean.setsecuritymanager(securitymanager); Map<String,String> filtermap = new LinkedHashMap<>(); // 认证过滤器 filtermap.put("/product/toadd", "anon"); filtermap.put("/**", "authc"); // 添加 shiro 过滤器 bean.setfilterchaindefinitionmap(filtermap); // 修改登录请求 bean.setloginurl("/tologin"); return bean; 4.4. 编写 Shiro 的认证操作 <!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> 登录页面 </title> </head> <body> <h3> 用户登录 </h3> <font color="red"></font> <form method="post" action="/user/login"> 用户名 :<input type="text" name="name"/><br/>

密码 :<input type="password" name="password"/><br/> <input type="submit" value=" 登录 "> </form> </body> </html> 4.4.1. 编写 UserController package cn.sm1234.controller; import javax.servlet.http.httpservletrequest; import org.apache.shiro.securityutils; import org.apache.shiro.authc.authenticationtoken; import org.apache.shiro.authc.incorrectcredentialsexception; import org.apache.shiro.authc.unknownaccountexception; import org.apache.shiro.authc.usernamepasswordtoken; import org.apache.shiro.subject.subject; import org.springframework.stereotype.controller; import org.springframework.ui.model; import org.springframework.web.bind.annotation.requestmapping; import cn.sm1234.domain.user; @Controller @RequestMapping("/user") public class UserController { /** * 登录

*/ @RequestMapping("/login") public String login(user user,httpservletrequest request,model model){ // 使用 shiro 进行登录 Subject subject = SecurityUtils.getSubject(); AuthenticationToken token = new UsernamePasswordToken(user.getName(), user.getpassword()); try { subject.login(token); // 登录成功 User dbuser = (User)subject.getPrincipal(); request.getsession().setattribute("username", dbuser.getname()); return "redirect:/index"; catch (UnknownAccountException e) { model.addattribute("msg", " 用户名不存在 "); return "login"; catch (IncorrectCredentialsException e) { model.addattribute("msg", " 密码 "); return "login";

4.4.2. 编写 MyReal 的代码 // 认证 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // 1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken) arg0; // 模拟数据库的密码 String name = "jack"; String password = "1234"; if (!token.getusername().equals(name)) { // 用户不存在 return null; User dbuser = new User(); dbuser.setname(name); dbuser.setpassword(password); // 返回密码 return new SimpleAuthenticationInfo(dbUser, dbuser.getpassword(), ""); 4.4.3. 登录页面提示 <!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> 登录页面 </title> </head> <body> <h3> 用户登录 </h3> <font color="red" th:text="${msg"></font> <form method="post" action="/user/login"> 用户名 :<input type="text" name="name"/><br/> 密码 :<input type="password" name="password"/><br/> <input type="submit" value=" 登录 "> </form> </body> </html> 4.5. 编写 shiro 的授权操作 /** * 1. 创建 ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shirofilterfactorybean(defaultwebsecuritymanager securitymanager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 关联 SecurityManager bean.setsecuritymanager(securitymanager);

Map<String,String> filtermap = new LinkedHashMap<>(); // 认证过滤器 filtermap.put("/product/toadd", "anon"); // 放行登录页面 filtermap.put("/user/login", "anon"); // 授权过滤器 filtermap.put("/product/toadd", "perms[product:add]"); filtermap.put("/product/toupdate", "perms[product:update]"); filtermap.put("/**", "authc"); // 添加 shiro 过滤器 bean.setfilterchaindefinitionmap(filtermap); // 修改登录请求 bean.setloginurl("/tologin"); // 添加未授权提示页面 bean.setunauthorizedurl("/unauth"); return bean; @Override protected AuthorizationInfo dogetauthorizationinfo(principalcollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addstringpermission("product:add");

return info; 4.6. 整合 MyBatis 4.6.1. 导入 mybatis 相关坐标 <!-- SpringBoot 的 Mybatis 启动器 --> <dependency> <groupid>org.mybatis.spring.boot</groupid> <artifactid>mybatis-spring-boot-starter</artifactid> <version>1.1.1</version> </dependency> <!-- druid 连接池 --> <dependency> <groupid>com.alibaba</groupid> <artifactid>druid</artifactid> <version>1.0.9</version> </dependency> <!-- mysql --> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> </dependency> 4.6.2. 配置 application.properties spring.datasource.url=jdbc:mysql://localhost:3306/shiro

spring.datasource.driverclassname=com.mysql.jdbc.driver spring.datasource.username=root spring.datasource.password=root spring.datasource.type=com.alibaba.druid.pool.druiddatasource mybatis.type-aliases-package=cn.sm1234.domain 4.6.3. 编写 Mapper 接口和映射 4.6.3.1. Mapper 接口 package cn.sm1234.dao; import cn.sm1234.domain.user; public interface UserMapper { public User findbyname(string name); 4.6.3.2. Mapper 映射 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//dtd Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.sm1234.dao.usermapper"> <select id="findbyname" parametertype="string" resulttype="user">

SELECT id, NAME, PASSWORD FROM shiro.t_user where name = #{value </select> </mapper> 4.6.4. 修改 ShiroApplication 启动类 @SpringBootApplication @MapperScan("cn.sm1234.dao") public class ShiroApplication { public static void main(string[] args) { SpringApplication.run(ShiroApplication.class, args); 4.6.5. 测试环境 package cn.sm1234.test; import javax.annotation.resource; import org.junit.test; import org.junit.runner.runwith;

import org.springframework.boot.test.context.springboottest; import org.springframework.test.context.junit4.springjunit4classrunner; import cn.sm1234.shiroapplication; import cn.sm1234.dao.usermapper; import cn.sm1234.domain.user; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes=ShiroApplication.class) public class UserMapperTest { @Resource private UserMapper usermapper; @Test public void testfindbyname(){ User user = usermapper.findbyname("eric"); System.out.println(user); 4.7. 改造为动态认证和授权 public class MyRealm extends AuthorizingRealm { @Resource private UserService userservice; @Override

protected AuthorizationInfo dogetauthorizationinfo(principalcollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //info.addstringpermission("product:add"); // 得到当前用户 Subject subject = SecurityUtils.getSubject(); User dbuser = (User)subject.getPrincipal(); List<String> perms = userservice.findpermissionbyuserid(dbuser.getid()); if(perms!=null){ for (String perm : perms) { if(!stringutils.isempty(perm)){ info.addstringpermission(perm); return info; // 认证 @Override protected AuthenticationInfo dogetauthenticationinfo(authenticationtoken arg0) throws AuthenticationException { // 1. 获取用户输入的账户信息 UsernamePasswordToken token = (UsernamePasswordToken) arg0;

/*// 模拟数据库的密码 String name = "jack"; String password = "1234"; if (!token.getusername().equals(name)) { // 用户不存在 return null; User dbuser = new User(); dbuser.setname(name); dbuser.setpassword(password);*/ User dbuser = userservice.findbyname(token.getusername()); if(dbuser==null){ // 用户不存在 return null; // 返回密码 return new SimpleAuthenticationInfo(dbUser, dbuser.getpassword(), ""); 4.8. Thymeleaf 整合 Shiro 权限标签 4.8.1. 导入整合坐标 <!-- thymeleaf 整合 shiro 标签 -->

<dependency> <groupid>com.github.theborakompanioni</groupid> <artifactid>thymeleaf-extras-shiro</artifactid> <version>2.0.0</version> </dependency> 4.8.2. 配置 ShiroConfig 在类中添加一个方法 : /** * 整合 Shiro 标签 */ @Bean public ShiroDialect shirodialect(){ return new ShiroDialect(); 4.8.3. 在 html 页面使用 shiro 标签 <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title> 商品管理系统后台主页 </title> </head> <body> <h3> 商品管理系统后台主页 </h3>

当前用户名 :<span th:text="${session.username"></span> <hr/> <span shiro:haspermission="product:add"> <a href="/product/toadd"> 商品添加 </a><br/> </span> <span shiro:haspermission="product:update"> <a href="/product/toupdate"> 商品修改 </a><br/> </span> <span shiro:haspermission="product:list"> <a href="/product/tolist"> 商品列表 </a><br/> </span> </body> </html> 4.9. Shiro 注销功能 4.9.1. idnex.hmtl <a href="/user/logout"> 注销 </a> 4.9.2. 编写 UserController /** * 注销方法 */ @RequestMapping("/logout")

public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); //shiro 底层删除 session 的会话信息 return "redirect:/tologin"; 4.10. 实现 RememberMe 功能 4.10.1. 配置 ShiroConfig 类 /** * Shiro 的配置类 * @author lenovo * */ @Configuration public class ShiroConfig { /** * 1. 创建 ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shirofilterfactorybean(defaultwebsecuritymanager securitymanager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 关联 SecurityManager bean.setsecuritymanager(securitymanager); Map<String,String> filtermap = new LinkedHashMap<>();

// 认证过滤器 filtermap.put("/product/toadd", "perms[product:add]"); // 放行登录页面 filtermap.put("/user/login", "anon"); // 授权过滤器 filtermap.put("/product/tolist", "perms[product:list]"); filtermap.put("/product/toupdate", "perms[product:update]"); // 添加 user 过滤器 filtermap.put("/index", "user"); // /index 的请求只要使用 rememberme 功能, 就可 以访问了 filtermap.put("/**", "authc"); // 添加 shiro 过滤器 bean.setfilterchaindefinitionmap(filtermap); // 修改登录请求 bean.setloginurl("/tologin"); // 添加未授权提示页面 bean.setunauthorizedurl("/unauth"); return bean; /** * 2. 创建 SecurityManager */ @Bean public DefaultWebSecurityManager defaultwebsecuritymanager(myrealm realm,cookieremembermemanager remembermemanager){

DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); // 关联 realm manager.setrealm(realm); // 关联 remembermemanager manager.setremembermemanager(remembermemanager); return manager; // 创建 CookieRememberMeManager @Bean public CookieRememberMeManager cookieremembermemanager(simplecookie cookie){ CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setcookie(cookie); return manager; /** * RememberMe 的功能 */ // 创建 Cookie @Bean public SimpleCookie simplecookie(){ SimpleCookie cookie = new SimpleCookie("rememberMe"); // 设置 cookie 的时间长度 cookie.setmaxage(120); // 设置只读模型

cookie.sethttponly(true); return cookie; /** * 3. 创建 Realm */ @Bean public MyRealm myreal(){ MyRealm realm = new MyRealm(); return realm; /** * 整合 Shiro 标签 */ @Bean public ShiroDialect shirodialect(){ return new ShiroDialect(); 4.10.2. 修改 login.html <!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> 登录页面 </title> </head> <body> <h3> 用户登录 </h3> <font color="red" th:text="${msg"></font> <form method="post" action="/user/login"> 用户名 :<input type="text" name="name"/><br/> 密码 :<input type="password" name="password"/><br/> 是否记住我 :<input type="checkbox" name="rememberme" value="1"/><br/> <input type="submit" value=" 登录 "> </form> </body> </html> 4.10.3. 修改 UserController /** * 登录 */ @RequestMapping("/login") public String login(user user,string rememberme,httpservletrequest request,model model){ // 使用 shiro 进行登录 Subject subject = SecurityUtils.getSubject(); //AuthenticationToken token = new UsernamePasswordToken(user.getName(), user.getpassword());

UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getpassword()); // 设置 remenmberme 的功能 if(rememberme!=null && rememberme.equals("1")){ token.setrememberme(true); try { subject.login(token); // 登录成功 User dbuser = (User)subject.getPrincipal(); dbuser.getname()); request.getsession().setattribute("username", return "redirect:/index"; catch (UnknownAccountException e) { model.addattribute("msg", " 用户名不存在 "); return "login"; catch (IncorrectCredentialsException e) { model.addattribute("msg", " 密码错误 "); return "login"; 4.11. 自定义 Filter 找回 Session 信息 package cn.sm1234.filter;

import javax.servlet.servletrequest; import javax.servlet.servletresponse; import org.apache.shiro.session.session; import org.apache.shiro.subject.subject; import org.apache.shiro.web.filter.authc.formauthenticationfilter; import cn.sm1234.domain.user; /** * 自定义认证过滤器, 加入 RememberMe 的功能 * @author lenovo * */ public class UserFormAuthenticationFilter extends FormAuthenticationFilter { @Override protected boolean isaccessallowed(servletrequest request, ServletResponse response, Object mappedvalue) { Subject subject = getsubject(request, response); // 如果 isauthenticated 为 false 证明不是登录过的, 同时 isrememberd 为 true // 证明是没登陆直接通过记住我功能进来的 if (!subject.isauthenticated() && subject.isremembered()) { // 获取 session 看看是不是空的 Session session = subject.getsession(true); // 查看 session 属性当前是否是空的 if (session.getattribute("username") == null) {

// 如果是空的才初始化 User dbuser = (User)subject.getPrincipal(); // 存入用户数据 session.setattribute("username", dbuser.getname()); // 这个方法本来只返回 subject.isauthenticated() 现在我们加上 subject.isremembered() // 让它同时也兼容 remember 这种情况 return subject.isauthenticated() subject.isremembered(); 4.12. 使用 Shiro 的加密算法改造登录 /** * 登录 */ @RequestMapping("/login") public String login(user user,string rememberme,httpservletrequest request,model model){ // 使用 shiro 进行登录 Subject subject = SecurityUtils.getSubject(); //AuthenticationToken token = new UsernamePasswordToken(user.getName(), user.getpassword());

// 使用 Shiro 对密码进行加密 Md5Hash hash = new Md5Hash(user.getPassword(), user.getname(), 2); UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), hash.tostring()); // 设置 remenmberme 的功能 if(rememberme!=null && rememberme.equals("1")){ token.setrememberme(true); try { subject.login(token); // 登录成功 User dbuser = (User)subject.getPrincipal(); request.getsession().setattribute("username", dbuser.getname()); return "redirect:/index"; catch (UnknownAccountException e) { model.addattribute("msg", " 用户名不存在 "); return "login"; catch (IncorrectCredentialsException e) { model.addattribute("msg", " 密码错误 "); return "login";

4.13. Kaptcha 验证码 4.13.1. 导入 Kaptcha 坐标 <!-- 验证码 --> <dependency> <groupid>com.github.penggle</groupid> <artifactid>kaptcha</artifactid> <version>2.3.2</version> </dependency> 4.13.2. 编写 Kaptcha 配置类 package cn.sm1234.shiro; import java.util.properties; import org.springframework.context.annotation.bean; import org.springframework.stereotype.component; import com.google.code.kaptcha.impl.defaultkaptcha; import com.google.code.kaptcha.util.config; @Component public class KaptcharConfig { @Bean public DefaultKaptcha getdefaultkaptcha() { com.google.code.kaptcha.impl.defaultkaptcha defaultkaptcha = new com.google.code.kaptcha.impl.defaultkaptcha();

Properties properties = new Properties(); properties.setproperty("kaptcha.border", "yes"); properties.setproperty("kaptcha.border.color", "105,179,90"); properties.setproperty("kaptcha.textproducer.font.color", "blue"); properties.setproperty("kaptcha.image.width", "110"); properties.setproperty("kaptcha.image.height", "40"); properties.setproperty("kaptcha.textproducer.font.size", "30"); properties.setproperty("kaptcha.session.key", "code"); properties.setproperty("kaptcha.textproducer.char.length", "4"); properties.setproperty("kaptcha.textproducer.font.names", " 宋体, 楷体, 微软雅黑 "); Config config = new Config(properties); defaultkaptcha.setconfig(config); return defaultkaptcha; 4.13.3. KaptchaController package cn.sm1234.controller; import java.awt.image.bufferedimage; import java.io.bytearrayoutputstream; import javax.annotation.resource; import javax.imageio.imageio; import javax.servlet.servletoutputstream; import javax.servlet.http.httpservletrequest;

import javax.servlet.http.httpservletresponse; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; import com.google.code.kaptcha.impl.defaultkaptcha; @Controller public class KaptcharController { @Resource private DefaultKaptcha defaultkaptcha; @RequestMapping("/defaultKaptcha") public void defaultkaptcha(httpservletrequest httpservletrequest, HttpServletResponse httpservletresponse) throws Exception { byte[] captchachallengeasjpeg = null; ByteArrayOutputStream jpegoutputstream = new ByteArrayOutputStream(); try { // 生产验证码字符串并保存到 session 中 String createtext = defaultkaptcha.createtext(); httpservletrequest.getsession().setattribute("verifycode", createtext); // 使用生产的验证码字符串返回一个 BufferedImage 对象并转为 byte 写入到 byte 数组中 BufferedImage challenge = defaultkaptcha.createimage(createtext); ImageIO.write(challenge, "jpg", jpegoutputstream); catch (IllegalArgumentException e) { httpservletresponse.senderror(httpservletresponse.sc_not_found);

return; // 定义 response 输出类型为 image/jpeg 类型, 使用 response 输出流输出图片的 byte 数 组 captchachallengeasjpeg = jpegoutputstream.tobytearray(); httpservletresponse.setheader("cache-control", "no-store"); httpservletresponse.setheader("pragma", "no-cache"); httpservletresponse.setdateheader("expires", 0); httpservletresponse.setcontenttype("image/jpeg"); ServletOutputStream responseoutputstream = httpservletresponse.getoutputstream(); responseoutputstream.write(captchachallengeasjpeg); responseoutputstream.flush(); responseoutputstream.close(); 4.13.4. 在 login.html 使用验证码 <!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> 登录页面 </title> </head> <body> <h3> 用户登录 </h3>

<font color="red" th:text="${msg"></font> <form method="post" action="/user/login"> 用户名 :<input type="text" name="name"/><br/> 密码 :<input type="password" name="password"/><br/> 验证码 :<input type="text" name="code"/><img src="/defaultkaptcha" onclick="this.src='/defaultkaptcha?d='+new Date()"/> <br/> 是否记住我 :<input type="checkbox" name="rememberme" value="1"/><br/> <input type="submit" value=" 登录 "> </form> </body> </html> 4.13.5. 验证用户输入是否正确 /** * 登录 */ @RequestMapping("/login") public String login(user user,string rememberme,string code,httpservletrequest request,model model){ // 判断验证码是否正确 if(!stringutils.isempty(code)){ // 取出生成的验证码 String verifycode = (String)request.getSession().getAttribute("verifyCode"); if(!code.equals(verifycode)){ model.addattribute("msg", " 验证码输入有误 "); return "login";

// 使用 shiro 进行登录 Subject subject = SecurityUtils.getSubject(); //AuthenticationToken token = new UsernamePasswordToken(user.getName(), user.getpassword()); // 使用 Shiro 对密码进行加密 Md5Hash hash = new Md5Hash(user.getPassword(), user.getname(), 2); UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), hash.tostring()); // 设置 remenmberme 的功能 if(rememberme!=null && rememberme.equals("1")){ token.setrememberme(true); try { subject.login(token); // 登录成功 User dbuser = (User)subject.getPrincipal(); request.getsession().setattribute("username", dbuser.getname());

return "redirect:/index"; catch (UnknownAccountException e) { model.addattribute("msg", " 用户名不存在 "); return "login"; catch (IncorrectCredentialsException e) { model.addattribute("msg", " 密码错误 "); return "login";