当前位置: 首页 > 编程日记 > 正文

shiro多realm验证之——shiro实现不同身份使用不同Realm进行验证(转)

转自: http://blog.csdn.net/xiangwanpeng/article/details/54802509

(使用特定的realm实现特定的验证)

假设现在有这样一种需求:存在两张表user和admin,分别记录普通用户和管理员的信息。并且现在要实现普通用户和管理员的分开登录,即需要两个Realm——UserRealm和AdminRealm,分别处理普通用户和管理员的验证功能。 
  但是正常情况下,当定义了两个Realm,无论是普通用户登录,还是管理员登录,都会由这两个Realm共同处理。这是因为,当配置了多个Realm时,我们通常使用的认证器是shiro自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator,其中决定使用的Realm的是doAuthenticate()方法,源代码如下

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {assertRealmsConfigured();Collection<Realm> realms = getRealms();if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); } }

这段代码的意思是:当只有一个Realm时,就使用这个Realm,当配置了多个Realm时,会使用所有配置的Realm。 
  现在,为了实现需求,我会创建一个org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类,并重写doAuthenticate()方法,让特定的Realm完成特定的功能。如何区分呢?我会同时创建一个org.apache.shiro.authc.UsernamePasswordToken的子类,在其中添加一个字段loginType,用来标识登录的类型,即是普通用户登录,还是管理员登录。具体步骤如下: 
   
  第一步:创建枚举类LoginType用以记录登录的类型:

//登录类型
//普通用户登录,管理员登录
public enum LoginType {USER("User"),  ADMIN("Admin");private String type;private LoginType(String type) {this.type = type;}@Overridepublic String toString() {return this.type.toString();}
}

第二步:新建org.apache.shiro.authc.UsernamePasswordToken的子类CustomizedToken:

import org.apache.shiro.authc.UsernamePasswordToken;public class CustomizedToken extends UsernamePasswordToken {//登录类型,判断是普通用户登录,教师登录还是管理员登录private String loginType;public CustomizedToken(final String username, final String password,String loginType) {super(username,password);this.loginType = loginType;}public String getLoginType() {return loginType;}public void setLoginType(String loginType) {this.loginType = loginType;}
}

第三步:新建org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类CustomizedModularRealmAuthenticator:

import java.util.ArrayList;
import java.util.Collection;import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;/*** @author Alan_Xiang * 自定义Authenticator* 注意,当需要分别定义处理普通用户和管理员验证的Realm时,对应Realm的全类名应该包含字符串“User”,或者“Admin”。* 并且,他们不能相互包含,例如,处理普通用户验证的Realm的全类名中不应该包含字符串"Admin"。*/
public class CustomizedModularRealmAuthenticator extends ModularRealmAuthenticator {@Overrideprotected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)throws AuthenticationException {// 判断getRealms()是否返回为空
        assertRealmsConfigured();// 强制转换回自定义的CustomizedTokenCustomizedToken customizedToken = (CustomizedToken) authenticationToken;// 登录类型String loginType = customizedToken.getLoginType();// 所有RealmCollection<Realm> realms = getRealms();// 登录类型对应的所有RealmCollection<Realm> typeRealms = new ArrayList<>();for (Realm realm : realms) {if (realm.getName().contains(loginType))typeRealms.add(realm);}// 判断是单Realm还是多Realmif (typeRealms.size() == 1)return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);elsereturn doMultiRealmAuthentication(typeRealms, customizedToken);}}

第四步:创建分别处理普通用户登录和管理员登录的Realm:

UserRealm:import javax.annotation.Resource;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.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;import com.ang.elearning.po.User;
import com.ang.elearning.service.IUserService;public class UserRealm extends AuthorizingRealm {@ResourceIUserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {User user = null;// 1. 把AuthenticationToken转换为CustomizedTokenCustomizedToken customizedToken = (CustomizedToken) token;// 2. 从CustomizedToken中获取emailString email = customizedToken.getUsername();// 3. 若用户不存在,抛出UnknownAccountException异常user = userService.getUserByEmail(email);if (user == null)throw new UnknownAccountException("用户不存在!");// 4.// 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo// 以下信息从数据库中获取// (1)principal:认证的实体信息,可以是email,也可以是数据表对应的用户的实体类对象Object principal = email;// (2)credentials:密码Object credentials = user.getPassword();// (3)realmName:当前realm对象的name,调用父类的getName()方法即可String realmName = getName();// (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同ByteSource credentialsSalt = ByteSource.Util.bytes(email);SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,realmName);return info;}}AdminRealm:import javax.annotation.Resource;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.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;import com.ang.elearning.po.Admin;
import com.ang.elearning.service.IAdminService;public class AdminRealm extends AuthorizingRealm {@Resourceprivate IAdminService adminService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// TODO Auto-generated method stubreturn null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {Admin admin = null;// 1. 把AuthenticationToken转换为CustomizedTokenCustomizedToken customizedToken = (CustomizedToken) token;// 2. 从CustomizedToken中获取usernameString username = customizedToken.getUsername();// 3. 若用户不存在,抛出UnknownAccountException异常admin = adminService.getAdminByUsername(username);if (admin == null)throw new UnknownAccountException("用户不存在!");// 4.// 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo// 以下信息从数据库中获取// (1)principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象Object principal = username;// (2)credentials:密码Object credentials = admin.getPassword();// (3)realmName:当前realm对象的name,调用父类的getName()方法即可String realmName = getName();// (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同ByteSource credentialsSalt = ByteSource.Util.bytes(username);SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,realmName);return info;}}

第五步:在spring配置文件中指定使用自定义的认证器:(其他配置略)

 <!-- 配置SecurityManager --><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="cacheManager" ref="cacheManager" /><property name="authenticator" ref="authenticator"></property><!-- 可以配置多个Realm,其实会把realms属性赋值给ModularRealmAuthenticator的realms属性 --><property name="realms"><list><ref bean="userRealm" /><ref bean="adminRealm"/></list></property></bean><!-- 配置使用自定义认证器,可以实现多Realm认证,并且可以指定特定Realm处理特定类型的验证 --><bean id="authenticator" class="com.ang.elearning.shiro.CustomizedModularRealmAuthenticator"><!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 --><property name="authenticationStrategy"><bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean></property></bean><!-- 配置Realm --><bean id="userRealm" class="com.ang.elearning.shiro.UserRealm"><!-- 配置密码匹配器 --><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><!-- 加密算法为MD5 --><property name="hashAlgorithmName" value="MD5"></property><!-- 加密次数 --><property name="hashIterations" value="1024"></property></bean></property></bean><bean id="adminRealm" class="com.ang.elearning.shiro.AdminRealm"><!-- 配置密码匹配器 --><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><!-- 加密算法为MD5 --><property name="hashAlgorithmName" value="MD5"></property><!-- 加密次数 --><property name="hashIterations" value="1024"></property></bean></property></bean>

第六步:配置控制器: 
   

 UserController:@Controller
@RequestMapping("/user")
public class UserController {private static final String USER_LOGIN_TYPE = LoginType.USER.toString();@Resourceprivate IUserService userService;@RequestMapping(value = "login", method = RequestMethod.POST)public String login(@RequestParam("email") String email, @RequestParam("password") String password) {Subject currentUser = SecurityUtils.getSubject();if (!currentUser.isAuthenticated()) {CustomizedToken customizedToken = new CustomizedToken(email, password, USER_LOGIN_TYPE);customizedToken.setRememberMe(false);try {currentUser.login(customizedToken);return "user/index";} catch (IncorrectCredentialsException ice) {System.out.println("邮箱/密码不匹配!");} catch (LockedAccountException lae) {System.out.println("账户已被冻结!");} catch (AuthenticationException ae) {System.out.println(ae.getMessage());}}return "redirect:/login.jsp";}
}AdminController:@Controller
@RequestMapping("/admin")
public class AdminController {private static final String ADMIN_LOGIN_TYPE = LoginType.ADMIN.toString();@RequestMapping(value="/login",method=RequestMethod.POST)public String login(@RequestParam("username") String username,@RequestParam("password") String password){Subject currentUser = SecurityUtils.getSubject();if(!currentUser.isAuthenticated()){CustomizedToken customizedToken = new CustomizedToken(username, password, ADMIN_LOGIN_TYPE);customizedToken.setRememberMe(false);try {currentUser.login(customizedToken);return "admin/index";} catch (IncorrectCredentialsException ice) {System.out.println("用户名/密码不匹配!");} catch (LockedAccountException lae) {System.out.println("账户已被冻结!");} catch (AuthenticationException ae) {System.out.println(ae.getMessage());}}return "redirect:/login.jsp";}
}

测试页面:login.jsp

<body><form action="${pageContext.request.contextPath }/user/login"method="POST">邮箱:<input type="text" name="email"> <br><br> 密码:<input type="password" name="password"> <br><br> <input type="submit" value="用户登录"></form><br><br><form action="${pageContext.request.contextPath }/admin/login"method="POST">用户名:<input type="text" name="username"> <br><br> 密 码:<input type="password" name="password"> <br><br> <input type="submit" value="管理员登录"></form>
</body>

这就实现了UserRealm用以处理普通用户的登录验证,AdminRealm用以处理管理员的登录验证。 
  如果还需要添加其他类型,例如,需要添加一个教师登录模块,只需要再新建一个TeacherRealm,并且在枚举类loginType中添加教师的信息,再完成其他类似的配置即可。

   

      2018-01-03 17:4910楼

demo : https://github.com/xiangwanpeng/e_learning

转载于:https://www.cnblogs.com/YLQBL/p/8193960.html

相关文章:

前端开发中的性能那点事

前端开发中的性能那点事&#xff08;一&#xff09;巧用xdebug 前言&#xff1a; 在我们平时的php开发中&#xff0c;一个大的项目经过长时间的积累以后你会发现性能越来越慢&#xff0c;而性能到底消耗在了什么地方&#xff0c;常常是一个令人头疼的问题&#xff0c;function…

运动目标检测ViBe算法

一、运动目标检测简介 视频中的运动目标检测这一块现在的方法实在是太多了。运动目标检测的算法依照目标与摄像机之间的关系可以分为静态背景下运动检测和动态背景下运动检测。先简单从视频中的背景类型来讨论。 静态背景下的目标检测&#xff0c;就是从序列图像中…

急缺开源人才怎么办?来看看大厂和高校怎么解决

开源&#xff0c;是数字中国建设的热点领域之一&#xff0c;也是数字经济时代的基础。在我国加速实现数字化转型的背景下&#xff0c;开源已经从个人行为、企业行为发展到了国家新基建的战略层面。它打破传统的组织架构与商业教条&#xff0c;彻底颠覆了工业经济时代的运转逻辑…

经验分享 | Burpsuite抓取非HTTP流量

使用Burp对安卓应用进行渗透测试的过程中&#xff0c;有时候会遇到某些流量无法拦截的情况&#xff0c;这些流量可能不是HTTP协议的&#xff0c;或者是“比较特殊”的HTTP协议(以下统称非HTTP流量)。遇到这种情况&#xff0c;大多数人会选择切换到Wireshark等抓包工具来分析。下…

Xdebug 配置

第一部分&#xff1a; 安装预编译模块安装预编译模块是很容易的。只需要将它们放到一个目录中&#xff0c;并将下面的内容添加到php.ini中&#xff1a;&#xff08;不要忘记更改路径和文件名为你自己的值&#xff0c;并确信你使用的是完整路&#xff09;zend_extension_ts &qu…

yield学习续:yield return迭代块在Unity3D中的应用——协程

必读好文推荐&#xff1a; Unity协程&#xff08;Coroutine&#xff09;原理深入剖析 Unity协程&#xff08;Coroutine&#xff09;原理深入剖析再续 上面的文章说得太透彻&#xff0c;所以这里就记一下自己的学习笔记了。 首先要说明的是&#xff0c;协程并不是线程&#xff0…

从神经元谈到深度神经网络

作者 | 泳鱼来源 | 算法进阶本文将从神经元到逻辑回归模型结构&#xff0c;并将其扩展到深度深度网络模型。一、谈谈智慧对于人类智慧奥秘的探索&#xff0c;不同时代、学科背景的人对于智慧的理解及其实现方法有着不同的思想主张。有的主张用显式逻辑体系搭建人工智能系统&…

【阿里聚安全·安全周刊】Intel芯片级安全漏洞事件|macOS存在漏洞

关键词&#xff1a;Intel漏洞丨mac OS漏洞丨三星漏洞丨安卓安全丨CPU漏洞丨phpMyAdmin漏洞丨iOS设备|安卓恶意软件检测|Burpsuite 本周资讯top3 【Intel漏洞】芯片级安全漏洞后续&#xff1a;谷歌表示不止Intel&#xff0c;每个1995年后的处理器都可能受影响 1月3日&#xff0c…

BigPipe:高性能的“流水线技术”网页

原文地址&#xff1a;http://www.facebook.com/note.php?note_id389414033919 译文地址&#xff1a;http://isd.tencent.com/?p2419 作者&#xff1a;蒋长浩 Facebook的网站速度做为最关键的公司任务之一。在2009年&#xff0c;我们成功地实现了Facebook网站速度提升两倍 。…

超硬核全套Java视频教程(学习路线+免费视频+配套资料)

文内福利&#xff0c;扫码免费领取Hello&#xff0c;各位锋迷们&#xff0c;我是小千。很多学习Java的小伙伴都在找的全套免费java视频教程&#xff0c;这里全都有&#xff0c;资料齐全&#xff0c;拿来吧你&#xff01;零基础学Java的学习路线图是怎样的&#xff1f;&#xff…

手机触屏滑动图片切换插件swiper.js

今天给大家分享一款手机触屏滑动图片切换插件swiper.js是一款swiper手机触屏滑动图片幻灯片&#xff0c;适合各种尺寸。效果图如下&#xff1a; 在线预览 源码下载 实现的代码。 html代码&#xff1a; <div style"max-width: 640px; margin: 0 auto;"><di…

Nginx防盗链,Nginx访问控制, Nginx解析php相关配置, Nginx代理

2019独角兽企业重金招聘Python工程师标准>>> Nginx防盗链 Nginx防盗链配置需要与不记录日志和过期时间结合在一起&#xff0c;因为都用到了location。 打开配置文件&#xff0c;注释掉一部分内容&#xff08;红框上方&#xff09;&#xff0c;添加内容&#xff08;红…

web高性能开发系列随笔

在BlogJava里写了一些关于高性能WEB开发的随笔&#xff0c;因为都是跟前端技术相关(html,http,js,css等)&#xff0c;所以也贴到博客园来&#xff0c;吸收下人气。 1、 HTTP服务器. 2、性能测试工具推荐 3、 图片篇. 4、 如何加载JS&#xff0c;JS应该放在什么位置. 5、…

《Effective C++》第8章 定制new和delete-读书笔记

章节回顾&#xff1a; 《Effective C》第1章 让自己习惯C-读书笔记 《Effective C》第2章 构造/析构/赋值运算&#xff08;1&#xff09;-读书笔记 《Effective C》第2章 构造/析构/赋值运算&#xff08;2&#xff09;-读书笔记 《Effective C》第3章 资源管理&#xff08;1&am…

观点:AI 与自动化是矛盾的

作者&#xff1a;cerebralab.com译者&#xff1a;张雨佳原文标题&#xff1a;AI and automation are at odds想象一下&#xff0c;我们生活在一个完美、和谐的地方&#xff0c;所有人在某一天同意让电脑代替人类驾驶汽车&#xff0c;而不是像现在逐步推进自动驾驶。那么&#x…

kaggle之数据分析从业者用户画像分析

数据为kaggle社区发布的数据分析从业者问卷调查分析报告&#xff0c;其中涵盖了关于该行业不同维度的问题及调查结果。本文的目的为提取有用的数据&#xff0c;进行描述性展示。帮助新从业的人员更全方位地了解这个行业。 参考学习视频:http://www.tianshansoft.com/ 数据集&am…

mysql读写分离(MySQL Proxy 安装和使用)

一、必备软件&#xff1a; 1、LUA 可以去LUA的官方下载&#xff1a;www.lua.org 2、MySQL Proxy 这里有好多二进制版本。 http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/ 或者去MYSQL官方下载源代码。 3、测试过程中取消了B和C的REPLICATION。这样SQL语句…

微软副总裁、Kubernetes 头号贡献者的他,给云原生带来奇迹!

我们做了一个街头采访&#xff0c;调查路人眼中的程序员到底是怎样的&#xff1f;提到程序员&#xff0c;大家似乎都有刻板印象&#xff1a;总是格子衬衫牛仔裤双肩包打扮&#xff0c;总是埋头敲代码&#xff0c;加班是常态……谁说程序员呆板木讷&#xff0c;只会埋头敲一行行…

查询Oracle中字段名带.的数据

SDE中的TT_L线层会有SHAPE.LEN这样的字段&#xff0c;使用&#xff1a; SQL>select shape.len from tt_l; 或 SQL>select t.shape.len from tt_l t; 是查询不出来的。 需要这样查询&#xff1a; SQL>select t."SHAPE"."LEN" from tt_l t; 转载于:…

再谈session共享

之前一篇已经写过了《springboot中redis的使用和分布式session共享问题》&#xff0c;但是示例不完全&#xff0c;本文加以完善。 使用spring-session-data-redis解决session共享&#xff0c;而不需要再引入其他jar即可 集成简单&#xff0c;上手迅速。 项目结构 1.pngpom <…

使用MySQL Proxy解决MySQL主从同步延迟

MySQL的主从同步机制非常方便的解决了高并发读的应用需求&#xff0c;给Web方 面开发带来了极大的便利。但这种方式有个比较大的缺陷在于MySQL的同步机制是依赖Slave主动向Master发请求来获取数据的&#xff0c;而且由于服务器负 载、网络拥堵等方面的原因&#xff0c;Master与…

Python 操作 MongoDB 数据库!

作者 |黄伟呢来源 |数据分析与统计学之美MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的。先来看看MySQL与MongoDB 概念区别&#xff1a;今天的重点&#xff0c;就是要为大家讲述如何使用Python操…

Linux下用汇编输出Hello, world

下列是Intel汇编语法实现的 Hello, world!程序。 ;; hello.asm ;; nasm -f elf hello.asm; will output hello.o ;; ld -s -o hello hello.o;; section, same to segment segment .data ; 数据段声明, 下列代码将放在数据段中msg db "Hello, world!", 0xA ; 要…

利用bigpipe机制实现页面模块的异步渲染 chunked技术

bigpipe基于HTTP/1.1 支持的chunked编码&#xff0c;可以由浏览器接收到服务器发送的chunked块后&#xff0c;立即解析该块代码。因为chunked编码使消息主体成块发送&#xff0c;每块有自己的大小指示器&#xff0c;在所有的块之后会紧接着一个可选的包含实体头域的尾部。这种编…

hibernate 全面学习【lazy策略 】

2019独角兽企业重金招聘Python工程师标准>>> lazy策略可以用在&#xff1a; * <class>标签上&#xff1a;可以取值true/false * <property>标签上&#xff0c;可以取值true/false&#xff0c;这个特性需要类增强 * <set>/<list>等集合上…

深度学习发现古人类遗址,AI 考古比胡八一更高效

作者 |神经星星来源 |HyperAI超神经By 超神经内容一览&#xff1a;伊利诺伊州立大学人类学专业考古方向的研究人员&#xff0c;将空间遥感技术和深度学习应用于古人类遗址的发掘和研究。关键词&#xff1a;考古 遥感 机器视觉考古&#xff0c;一直是个神秘又充满吸引力的话题。…

linux resource

1. centos repo https://centos.pkgs.org/转载于:https://www.cnblogs.com/gojoin/p/8241068.html

MySQL 水平分区方案Spock Proxy

Spock Proxy 是由实际项目产生的一个开源项目&#xff08;Spock是Rails的应用&#xff0c;Speck Proxy应当可用于Rails之外的&#xff0c;例如PHP或.NET&#xff09;&#xff0c;基于MySQL Proxy开发&#xff0c;是MySQL Proxy的一个分支&#xff0c;支持range-based horizonta…

卷学历、卷加班……程序员拥有什么能力才能破局内卷?

全世界的天才程序员都在疯狂的造轮子&#xff0c;其它程序员只能被动的学习轮子&#xff0c;这简直就像一场绝地求生。程序员行业的内卷已是有目共睹&#xff0c;选择程序员作为职业的人越来越多&#xff1b;大厂对程序员的学历要求越来越高&#xff1b;程序员工作加班越来越严…

LeetCode - Maximum Depth of Binary Tree

递归求二叉树的最大深度。 /*** Definition for binary tree* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val x; }* }*/ public class Solution {public int maxDepth(TreeNode root) {if(root null)return 0…