转自: 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

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

  1. Shiro的多Realm验证的实现--shiro实现不同身份使用不同Realm进行验证

    如果要看多Realm进行授权: 请参看   https://blog.csdn.net/u013294097/article/details/90066869 假设现在有这样一种需求:存在两张表use ...

  2. Shiro验证策略-shiro自定义实现Realm来实现身份验证-shiro散列加密算法-shiro授权-shiro自定义Realm实现授权

    Shiro验证策略 Authentication Strategy:认证策略,在shiro中有三种认证策略: AtleastOneSuccessfulStrategy[默认] 如果一个或多个Realm ...

  3. Shiro系列(三)--- Shiro身份验证和授权

    继续我们shiro系列博客相关的学习笔记,各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟.多谢! 目录 身份验证 示例 创建SecurityManager ...

  4. 权限验证框架Shiro

    权限验证框架Shiro: Shiro简介 什么是Shiro: shiro是一个强大易用的Java安全框架,提供了认证,授权,加密,回话管理等功能: 认证(Authentication):用户身份识别, ...

  5. shiro系列一、认识shiro

    Apache Shiro是Java的一个安全框架.目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大 ...

  6. shiro教程(4)-shiro与项目集成开发

    1 shiro与项目集成开发 这里我们主要以用户登录的例子来演示,先给出一个时序图: 点击打开链接(点击查看) 1.1 shiro与spring web项目整合 shiro与springweb项目整合 ...

  7. shiro访问html没有验证码,Shiro在web应用中实现验证码、回显登录失败信息

    目录结构: 概述 扩展shiro认证 验证码工具 验证码servlet 配置文件修改 修改登录页面 测试验证 [一].概述 本文简单讲述在web应用整合shiro后,如何实现登录验证码认证的功能. [ ...

  8. 文件用户Apache shiro学习笔记+ spring整合shiro (一)

    改章节朋友在青岛游玩的时候突然想到的...这两天就有想写几篇关于文件用户的博客,所以回家到之后就奋笔疾书的写出来发表了 Apache Shiro官网:http://shiro.apache.org/ ...

  9. Shiro教程(九)Shiro JSP标签的使用。

    Shiro  提供了 JSP  的一套 JSTL  标签,用于做 JSP  页面做权限控制的.可以控制一些按钮和一些超链接,或者一些显示内容. Freemarker Shiro标签讲解:https:/ ...

最新文章

  1. dell 服务器 加ssd硬盘,DELL服务器加SSD硬盘.doc
  2. TableCellRenderer TableCellEditor(三)
  3. WiFi Deauthenticated Reason Codes
  4. 实验一基于 的词法分析实验_[源码和文档分享]基于JAVA实现的基于DFA的词法分析程序...
  5. php开发者与composer的不得不说故事
  6. java array iterator_java数组遍历——iterator和for方法
  7. git:如何让不同开发者提交在同一条直线上
  8. 百度之星资格赛 J题 百度的新大厦
  9. sort()基本用法
  10. 北京大学公开课《数据结构与算法Python版》
  11. [MAE]Masked Autoencoders掩膜自编码器
  12. 记录安装tensorflow-gpu,版本选择问题,短平快解决战斗
  13. 蓝牙音乐SRC侧的安卓实现
  14. Qt:可视化UI设计
  15. 经常用电脑辐射大怎么办?这5个习惯可以防辐射
  16. 白话空间统计二十七:统计学七支柱之空间统计版本(二)聚合(2)
  17. 【Flink】浅谈Flink背压问题(1)
  18. 非常棒的loading制作教程
  19. 5.4. rc1,rc2,rc4
  20. php抓取html元素内容 采集网页

热门文章

  1. [LintCode] Maximum Subarray 最大子数组
  2. MYSQL添加新用户 MYSQL为用户创建数据库 MYSQL为新用户分配权限
  3. PARAMETERS 指令
  4. ⑨③-不能浪费拥有的年轻资本
  5. 体验Windows 7的Superbar
  6. 可伸缩系统的设计模式(译)
  7. 深挖数据价值 阿里云栖开年大会报道
  8. Camel之AsyncProcessor
  9. 设计模式六大原则(6)——开闭原则
  10. Struts 为什么使用插件