shiro多realm验证实现

一般 我们只会用一张user表的信息来登录,这样的话我们只需要一个userRealm令牌来管理这个认证功能。但是我现在想用两张表user 和 admin 来完成 用户 和管理员分开登录 那是不是就需要两个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。
为了实现两个realm令牌认证

第一、我们先创建一个枚举类LoginType用以记录登录的类型:
一个普通用户user 一个管理员admin

/**
 * 枚举类型
 * @author LSJ
 */
public enum LoginType
{
    USER("User"),  ADMIN("Admin");

    private String type;

    private LoginType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return this.type.toString();
    }
}

第二、创建一个CustomizedToken类继承shiro的UsernamePasswordToken类用来判断登陆的类型:

/**
 * 登录类型
 * @author LSJ
 */
public class CustomizedToken extends UsernamePasswordToken
{
    /**
     * 
     */
    private static final long serialVersionUID = -4890297065880698142L;

    //登录类型,判断是普通用户登录,还是管理员登录
    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;
    }
}

第三、创建一个CustomizedModularRealmAuthenticator类继承shiro的ModularRealmAuthenticator。这个方法就是用来判断执行多个realm的:

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

        // 判断是单Realm还是多Realm
        if (typeRealms.size() == 1)
            return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
        else
            return doMultiRealmAuthentication(typeRealms, customizedToken);
    }
}

如果只找到一个realm令牌就执行一个 , 如果找到两个就根据方法名来找相对应的令牌 所以 创建realm令牌类时 类名一定要对应自定义枚举类名

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

/**
 * AdminRealm
 * @author LSJ
 */
public class AdminRealm extends AuthorizingRealm
{
    @Inject
    private AdminService adminService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals )
    {
        return null;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) throws AuthenticationException
    {
        Admin admin = null;
        // 1. 把AuthenticationToken转换为CustomizedToken
        CustomizedToken customizedToken = (CustomizedToken) token;
        // 2. 从CustomizedToken中获取username
        String adminCode = customizedToken.getUsername();
        // 3. 若用户不存在,抛出UnknownAccountException异常
        admin = adminService.getAdminByCode( adminCode );
        if (admin == null){
            throw new UnknownAccountException("用户不存在!");
        }else if( admin.getState() == 0 )
        {
            throw new LockedAccountException( "账户被禁用" );
        }
        // 4.
        // 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo
        // 以下信息从数据库中获取
        // (1)principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
        Object principal = adminCode;
        // (2)credentials:密码
        Object credentials = admin.getAdminPwd();
        // (3)realmName:当前realm对象的name,调用父类的getName()方法即可
        String realmName = getName();
        // (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同
        ByteSource credentialsSalt = ByteSource.Util.bytes(adminCode);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,
                realmName);
        return info;
    }
}

==================
UserRealm 同样:

/**
 * UserRealm 
 * @author LSJ
 */
public class UserRealm extends AuthorizingRealm
{
    @Inject
    private UserServices userServices;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals )
    {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) throws AuthenticationException
    {
        User user = null;
        // 1. 把AuthenticationToken转换为CustomizedToken
        CustomizedToken customizedToken = (CustomizedToken) token;
        // 2. 从CustomizedToken中获取email
        String userCode = customizedToken.getUsername();
        // 3. 若用户不存在,抛出UnknownAccountException异常
        user = userServices.getLUser( userCode );
        if (user == null){
            throw new UnknownAccountException("用户不存在!");
        }else if( user.getState() == 0 )
        {
            throw new LockedAccountException( "账户被禁用" );
        }
        // 4.
        // 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为SimpleAuthenticationInfo
        // 以下信息从数据库中获取
        // (1)principal:认证的实体信息,可以是email,也可以是数据表对应的用户的实体类对象
        Object principal = userCode;
        // (2)credentials:密码
        Object credentials = user.getPassword();
        // (3)realmName:当前realm对象的name,调用父类的getName()方法即可
        String realmName = getName();
        // (4)盐值:取用户信息中唯一的字段来生成盐值,避免由于两个用户原始密码相同,加密后的密码也相同
        ByteSource credentialsSalt = ByteSource.Util.bytes(userCode);
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt,realmName);
        return info;
    }
}

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

	<!-- 配置SecurityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="sessionManager" ref="sessionManager" />
        <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="cn.com.security.filter.CustomizedModularRealmAuthenticator">
        <!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 -->
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
        </property>
    </bean>

	<!-- 配置Realm  简单配置 -->
	 <bean id="adminRealm" class="cn.com.security.filter.AdminRealm"> 
		<property name="adminService" ref="adminService"/>
    </bean> 
    <bean id="userRealm" class="cn.com.security.filter.UserRealm"> 
		<property name="userServices" ref="userServices"/>
    </bean>
    
    <!-- 上下两种方式配置 看需求选择一个就好-->
    
	<!-- 配置Realm 更安全配置 -->
	<!--
    <bean id="userRealm" class="cn.com.security.filter.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="cn.com.security.filter.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>
    -->

第六步、配置控制层:

@Controller
@RequestMapping("/user")
public class UserController {

    private static final String USER_LOGIN_TYPE = LoginType.USER.toString();

    @Resource
    private UserService 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";
    }

}

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

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值