Shiro学习笔记——认证与加密

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012525096/article/details/82591606

流程

  1. 获取当前的 Subject, 调用 SecurityUtils.getSubject();
  2. 测试当前的用户是否已经被认证,即是否已经登录。调用 Subject .isAuthenticated()
  3. 若没有被认证,则把用户名和密码封装为UsernamePasswordToken 对象。
    1)创建一个表单
    2)把请求提交到SpringMVC的Handler
    3)获取用户名和密码

  4. 执行登录:调用Subject的login(AuthenticationToken)方法

  5. 自定义Realm的方法,从数据库中获取对应的记录,返货给Shiro
    1)实际上需要继承org.apache.shiro.realm.AuthenticatingRealm
    2)实现doGetAuthenticationInfo(AuthenticationToken)方法
  6. 由Shiro完成对密码的比对。

实现

SpringMVC Handler

@Controller
@RequestMapping("/shiro")
public class ShiroHandler {

    @RequestMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
        Subject currentUser = SecurityUtils.getSubject();

        if (!currentUser.isAuthenticated()) {
            // 把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            // rememberme
            token.setRememberMe(true);
            try {
                System.out.println("1:" + token.hashCode());
                // 执行登录.
                currentUser.login(token);
            }
            // ... catch more exceptions here (maybe custom ones specific to your
            // application?
            // 所有认证时异常的父类.
            catch (AuthenticationException ae) {
                // unexpected condition? error?
                System.out.println("登录失败: " + ae.getMessage());
            }
        }
        return "redirect:/list.jsp";
    }
}

Realm

Realm的作用就是获取输入的username和password,根据username从数据库取出用户名对应实体的信息,构建相对应的信息,如查询成功则构建正常的AuthenticationInfo对象,失败则根据信息抛出对应的异常。
doGetAuthenticationInfo(AuthenticationToken token)中的token对象就是Handler中Subject.login()中的token对象。

public class ShiroRealm extends AuthenticatingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("doGetAuthenticationInfo: " + token.hashCode());
        // 1. 把AuthenticationToken 转换为UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        // 2. 把UsernamePasswordToken 中来获取username
        String username = upToken.getUsername();

        // 3. 调用数据库的方法,从数据库中查询username 对应的用户记录.
        System.out.println("从数据库中获取username:" + username + " 所对应的用户信息.");

        // 4.若用户不存在,则可以抛出UnknownAccountException 异常
        if ("unknown".equals(username)) {
            throw new UnknownAccountException("用户不存在");
        }

        // 5.根据用户信息的情况,决定是否需要抛出其他的AuthenticationException 异常.
        if ("monster".equals(username)) {
            throw new LockedAccountException("用户被锁定");
        }

        // 6.根据用户的情况,来构建AuthenticationInfo 对象并返回.
        // 通常使用的实现类是SimpleAuthenticationInfo
        // 以下信息是从数据库中获取的.
        // (1)principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
        Object principal = username;
        // (2)credentials:密码.
        Object credentials = "123456";
        // (3)realmName:当前realm 对象的name,调用父类的getName()方法
        String realmName = getName();

        AuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
        return info;
    }
}

密码比对

通过AuthenticatingRealmcredentialsMatcher属性来进行的密码的比对!
这里写图片描述

MD5加密

1、如何把一个字符串加密成MD5
2、替换当前Realm的credentialsMatcher属性,直接使用HashedCredentialsMatcher对象,并设置加密算法即可。
在applicationContext中,为Realm添加property属性,设置属性即可。

    <bean id="jdbcRealm" class="com.shen.shiro.realms.ShiroRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="MD5"></property>
            <property name="hashIterations" value="1024"></property>
        </bean>
    </property>
    </bean>

为其设置一个参数,类对象为HashedCredentialsMatcher,并为该类设置参数【加密算法,加密次数】。

MD5盐值加密

问题:为什么使用MD5 盐值加密?
即使两个人密码相同,也希望数据库存储的密码也不相同,如何做到?
(1)在doGetAuthenticationInfo方法返回值创建SimpleAuthenticationInfo对象的时候,需要使用SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)这个构造器。
(2)使用ByteSource.Util.bytes()来计算盐值。
(3)盐值需要唯一:一般使用随机字符串或user id
(4)使用new SimpleHash(algorithmName, source, salt, hashIterations);来计算盐值加密后的密码的值。

多Realm

(1)与第一个Realm类似,编写第二个Realm。
(2)在applicationContext.xml中,1)增加第二个Realm的Bean;2)增加一个org.apache.shiro.authc.pam.ModularRealmAuthenticator的Bean,负责配置多Realm,其中有一个属性为realms,使用list标签将多个Realm配置进去。3)将2中的authenticator配置到securityManager中。

    <!-- 3)将认证器配置到securityManager的属性中去 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
    </bean>
    <!-- 2)认证器的bean -->
    <bean id="authenticator" 
        class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
                <ref bean="secondRealm"/>
            </list>
        </property>
    </bean>
    <!-- 1)多个Realm的bean -->
    <bean id="jdbcRealm" class="com.shen.shiro.realms.ShiroRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="MD5"></property>
            <property name="hashIterations" value="1024"></property>
        </bean>
    </property>
    </bean>

     <bean id="secondRealm" class="com.shen.shiro.realms.SecondShiroRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="SHA1"></property>
            <property name="hashIterations" value="1024"></property>
        </bean>
    </property>
    </bean>

多Realm的认证策略

共有三种策略:
这里写图片描述
(1)FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略。
(2)AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,它将返回所有Realm身份验证成功的认证信息;
(3)AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
注:ModularRealmAuthenticator默认是AtLeastOneSuccessfulStrategy策略。

更改认证策略

在applicationContext.xml中的认证器设置一个属性

    <bean id="authenticator" 
        class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
                <ref bean="secondRealm"/>
            </list>
        </property>
        <!-- 设置认证策略 -->
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"/>
        </property>
    </bean>

猜你喜欢

转载自blog.csdn.net/u012525096/article/details/82591606