shiro基本使用和登录源码

shiro的主要功能为 拦截无权限管理的页面 需要接管登录服务 登录时设置加密方式

spring整合shiro基本配置

shiro的设置类

@Bean
public CustomRealm shiroReam() {
    CustomRealm customRealm = new CustomRealm();
    customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
    return customRealm;
}
@Bean
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(shiroReam());
    return securityManager;
}
/**
 * 密码加密验证配置
 *
 * @return hash凭证匹配器
 */
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    // 使用md5 算法进行加密
    hashedCredentialsMatcher.setHashAlgorithmName("MD5");
    // 设置散列次数: 意为加密几次
    hashedCredentialsMatcher.setHashIterations(ShiroUtil.hashIterations);

    return hashedCredentialsMatcher;
}
package com.hz.treasury.componet;

import com.hz.treasury.bean.Login;
import com.hz.treasury.bean.Permissions;
import com.hz.treasury.bean.Role;
import com.hz.treasury.bean.User;
import com.hz.treasury.exception.LoginFreezeException;
import com.hz.treasury.service.LoginService;
import lombok.extern.slf4j.Slf4j;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

/**
 * @author Hz
 */
@Slf4j
public class CustomRealm extends AuthorizingRealm {
    private LoginService loginService;


    @Autowired
    public void setLoginService(LoginService loginService) {
        this.loginService = loginService;
    }

    /**
     * 权限配置类
     *
     * @param principalCollection 收集信息
     * @return 授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String name = principalCollection.getPrimaryPrincipal().toString();
        User user = loginService.getLoginByUsername(name).getUser();
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        Role role = user.getCurrentRole();
        simpleAuthorizationInfo.addRole(role.getName());
        for (Permissions permissions : role.getPermissions()) {
            simpleAuthorizationInfo.addStringPermission(permissions.getName());
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 认证配置类
     *
     * @param authenticationToken 身份验证令牌
     * @return 身份验证信息
     * @throws AuthenticationException 身份验证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (!StringUtils.hasText((String) authenticationToken.getPrincipal())) {
            return null;
        }
        String name = authenticationToken.getPrincipal().toString();
        Login login = loginService.getLoginByUsername(name);
        if (login == null) {
            return null;
        } else if (!login.getState()) {
            throw new LoginFreezeException();
        } else {
            ByteSource salt = ByteSource.Util.bytes(login.getSalt());
            return new SimpleAuthenticationInfo(name, login.getPassword(), salt, getName());
        }
    }
}

shiro登录时验证过程源码

这时候在注册时用同样的格式存入密码

login.setPassword(ShiroUtil.salt(password, ByteSource.Util.bytes(login.getSalt())));

在登录时

subject.login(usernamePasswordToken);

即可完成登录

但是时怎么完成登录的呢,看看源码

调用了securityManager中的login方法实现类为DefaultSecurityManager

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
        info = authenticate(token);
    } catch (AuthenticationException ae) {
        try {
            onFailedLogin(token, ae, subject);
        } catch (Exception e) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an " +
                        "exception.  Logging and propagating original AuthenticationException.", e);
            }
        }
        throw ae; //propagate
    }

    Subject loggedIn = createSubject(token, info, subject);

    onSuccessfulLogin(token, info, loggedIn);

    return loggedIn;
}

token为我们传入的登录信息,传入的是UsernamePasswordToken类为AuthenticationToken的子类

info为正确的用户信息,如果验证不通过会抛出异常进入onFailedLogin 进入失败的Subject

如果验证通过则创建成功的Subject

authenticate方法进入了AbstractAuthenticator的authenticate方法

里面又调用了

    info = doAuthenticate(token);

实现类为ModularRealmAuthenticator

  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);
        }
    }

如果我们创建设置了Realms类且只有一个时进入doSingleRealmAuthentication,调用接口

AuthenticationInfo info = realm.getAuthenticationInfo(token);

实现类为AuthenticatingRealm

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
        //otherwise not cached, perform the lookup:
        info = doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {
            cacheAuthenticationInfoIfPossible(token, info);
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

    if (info != null) {
        assertCredentialsMatch(token, info);
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}

这里的token仍然为传入的账号密码

info为通过token中的username获取数据库中的登录相关信息如果账号存在则会调用cacheAuthenticationInfoIfPossible方法将信息缓存起来用于下次登录

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

这里有一个抽象方法,如果我们在设置创建CustomRealm时重写了这个方法,就会进入到我们重写的方法中,获取到一个AuthenticationInfo的子类进入 assertCredentialsMatch方法中对比

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = getCredentialsMatcher();
    if (cm != null) {
        if (!cm.doCredentialsMatch(token, info)) {
            //not successful - throw an exception to indicate this:
            String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
            throw new IncorrectCredentialsException(msg);
        }
    } else {
        throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
                "credentials during authentication.  If you do not wish for credentials to be examined, you " +
                "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
    }
}

/**
 * Interface implemented by classes that can determine if an AuthenticationToken's provided
 * credentials matches a corresponding account's credentials stored in the system.
 *
 * <p>Simple direct comparisons are handled well by the
 * {@link SimpleCredentialsMatcher SimpleCredentialsMatcher}.  If you
 * hash user's credentials before storing them in a realm (a common practice), look at the
 * {@link HashedCredentialsMatcher HashedCredentialsMatcher} implementations,
 * as they support this scenario.
 *
 * @see SimpleCredentialsMatcher
 * @see AllowAllCredentialsMatcher
 * @see Md5CredentialsMatcher
 * @see Sha1CredentialsMatcher
 * @since 0.1
 */
public interface CredentialsMatcher {

    /**
     * Returns {@code true} if the provided token credentials match the stored account credentials,
     * {@code false} otherwise.
     *
     * @param token   the {@code AuthenticationToken} submitted during the authentication attempt
     * @param info the {@code AuthenticationInfo} stored in the system.
     * @return {@code true} if the provided token credentials match the stored account credentials,
     *         {@code false} otherwise.
     */
    boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);

}

从注释可以看到有四种验证器
SimpleCredentialsMatcher, AllowAllCredentialsMatcher, Md5CredentialsMatcher, Sha1CredentialsMatcher

从注释看到通常用直接比较和hash对比 我们用的是hash对比

@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenHashedCredentials = hashProvidedCredentials(token, info);
    Object accountCredentials = getCredentials(info);
    return equals(tokenHashedCredentials, accountCredentials);
}
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
    Object salt = null;
    if (info instanceof SaltedAuthenticationInfo) {
        salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
    } else {
        //retain 1.0 backwards compatibility:
        if (isHashSalted()) {
            salt = getSalt(token);
        }
    }
    return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}

从info或token中获取盐值

protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
    String hashAlgorithmName = assertHashAlgorithmName();
    return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
}

对输入的密码加密然后对比密码,之后就是一系列的保存信息

其实他的登录也很简单,所以说我才能很顺利的读懂源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值