使用自定义Realm后如何进行认证及授权

在使用自定义Realme之后subject如何进行认证
securityManager配置
@Bean
public DefaultWebSecurityManager securityManager(AdminRealm adminRealm, WxRealm wxRealm,
                                                 DefaultWebSessionManager sessionManager,
                                                 CustomAuthenticator authenticator) {

    DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

    //添加realm
    ArrayList<Realm> realms = new ArrayList<>();
    realms.add(adminRealm);
    realms.add(wxRealm);
    defaultWebSecurityManager.setRealms(realms);

    defaultWebSecurityManager.setSessionManager(sessionManager);
    //此处设定的是自定义authenticator
    defaultWebSecurityManager.setAuthenticator(authenticator);
    return defaultWebSecurityManager;
}
@Bean
public CustomAuthenticator authenticator(AdminRealm adminRealm, WxRealm wxRealm) {
    CustomAuthenticator customAuthenticator = new CustomAuthenticator();
    ArrayList<Realm> realms = new ArrayList<>();
    realms.add(adminRealm);
    realms.add(wxRealm);
    customAuthenticator.setRealms(realms);
    return customAuthenticator;
}
@Component
public class CustomAuthenticator extends ModularRealmAuthenticator {


    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) {

        this.assertRealmsConfigured();
        Collection<Realm> originRealms = this.getRealms();
        MallToken mallToken = (MallToken) authenticationToken;
        String type = mallToken.getType();
        ArrayList<Realm> realms = new ArrayList<>();

        for (Realm originRealm : originRealms) {
            if (originRealm.getName().toLowerCase().contains(type)) {
                realms.add(originRealm);
            }
        }
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
    }
}

首先在controller中使用subject.login方法,传入token

@CountTime
@RequestMapping("login")
public Result login(@RequestBody LoginBo loginBo) {

    MallToken adminToken = new MallToken(loginBo.getUsername(), loginBo.getPassword(), "admin");
    Subject subject = SecurityUtils.getSubject();
    subject.login(adminToken);
    return Result.ok(subject.getSession().getId());
}

而Subject是一个接口,由DelegatingSubject类实现接口来具体实现login方法,而在第3行中又由this.securityManager调用login , 并传入token

public void login(AuthenticationToken token) throws AuthenticationException {
    this.clearRunAsIdentitiesInternal();
    Subject subject = this.securityManager.login(this, token);
    String host = null;
    PrincipalCollection principals;
    if (subject instanceof DelegatingSubject) {
        DelegatingSubject delegating = (DelegatingSubject)subject;
        principals = delegating.principals;
        host = delegating.host;
    } else {
        principals = subject.getPrincipals();
    }

    if (principals != null && !principals.isEmpty()) {
        this.principals = principals;
        this.authenticated = true;
        if (token instanceof HostAuthenticationToken) {
            host = ((HostAuthenticationToken)token).getHost();
        }

        if (host != null) {
            this.host = host;
        }

        Session session = subject.getSession(false);
        if (session != null) {
            this.session = this.decorate(session);
        } else {
            this.session = null;
        }

    } else {
        String msg = "Principals returned from securityManager.login( token ) returned a null or empty value.  This value must be non null and populated with one or more elements.";
        throw new IllegalStateException(msg);
    }
}

此处this.securityManager为DelegatingSubject类中

protected transient SecurityManager securityManager;

此SecurityManager是一个接口

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
    Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;

    void logout(Subject var1);

    Subject createSubject(SubjectContext var1);
}

由DefaultSecurityManager类对login方法做了具体实现 , 其中第4行又调用了authenticate方法 , 传入token。但是DefaultSecurityManager本身并没有实现authenticate(token)方法,此处调用方法时调用的是其父类(AuthenticatingSecurityManager)方法

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info;
    try {
        info = this.authenticate(token);
    } catch (AuthenticationException var7) {
        AuthenticationException ae = var7;

        try {
            this.onFailedLogin(token, ae, subject);
        } catch (Exception var6) {
            if (log.isInfoEnabled()) {
                log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
            }
        }

        throw var7;
    }

    Subject loggedIn = this.createSubject(token, info, subject);
    this.onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    return this.authenticator.authenticate(token);
}

this.authenticator再次调用authenticate 。因为之前的securityManager配置,所以此处的this.authenticator为CustomeAuthenticator类

private Authenticator authenticator = new ModularRealmAuthenticator();
public void setAuthenticator(Authenticator authenticator) throws IllegalArgumentException {
        if (authenticator == null) {
            String msg = "Authenticator argument cannot be null.";
            throw new IllegalArgumentException(msg);
        } else {
            this.authenticator = authenticator;
        }
    }
}
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
    return this.authenticator.authenticate(token);
}

而CustomeAuthenticator又重写了authenticate方法,直到最后执行this.doSingleRealmAuthentication((Realm)realms.iterator().next(),authenticationToken)由父类ModularRealmAuthenticator执行

public class CustomAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) {

        this.assertRealmsConfigured();
        Collection<Realm> originRealms = this.getRealms();
        MallToken mallToken = (MallToken) authenticationToken;
        String type = mallToken.getType();
        ArrayList<Realm> realms = new ArrayList<>();

        for (Realm originRealm : originRealms) {
            if (originRealm.getName().toLowerCase().contains(type)) {
                realms.add(originRealm);
            }
        }
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
    }
}
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
    if (!realm.supports(token)) {
        String msg = "Realm [" + realm + "] does not support authentication token [" + token + "].  Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type.";
        throw new UnsupportedTokenException(msg);
    } else {
        AuthenticationInfo info = realm.getAuthenticationInfo(token);
        if (info == null) {
            String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "].";
            throw new UnknownAccountException(msg);
        } else {
            return info;
        }
    }
}

执行到realm.getAuthenticationInfo(token),Realm是一个接口,此方法由AuthenticatingRealm实现,且被final关键字修饰,所以不能被重写。因为这个项目并没有加入缓存机制,所以会执行第5行info = this.doGetAuthenticationInfo(token),如有缓存机制,则执行this.assertCredentialsMatch(token, info);

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
    if (info == null) {
        info = this.doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {
            this.cacheAuthenticationInfoIfPossible(token, info);
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

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

    return info;
}

这是一个抽象方法,因为是在adminRealm上调用,所以会执行AdminRealm的doGetAuthenticationInfo方法,最后返回SimpleAuthenticationInfo

protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

    MallToken adminToken = (MallToken) authenticationToken;
    String username = adminToken.getUsername();
    //从数据库中取出数据
    Admin admin = adminMapper.selectAdminByUsername(username);
    String credential = admin.getPassword();
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, credential, this.getName());
    return authenticationInfo;
}

不断返回之后,在这里进行加密匹配,也可以自定义明文匹配

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
    if (info == null) {
        info = this.doGetAuthenticationInfo(token);
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
        if (token != null && info != null) {
            this.cacheAuthenticationInfoIfPossible(token, info);
        }
    } else {
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
    }

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

    return info;
}
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = this.getCredentialsMatcher();
    if (cm != null) {
        if (!cm.doCredentialsMatch(token, info)) {
            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.");
    }
}

此处doCredentialsMatch(token, info)有四种密码器匹配模式,具体调用哪种还有待验证。

AllowAllCredentialsMatcher为有用户名就通过,SimpleCredentialsMatcher为简单的相等,剩余两种采用加密算法进行密码验证。

在这里插入图片描述

至此,认证的基本流程就结束了。

在使用自定义Realme之后subject如何进行授权

授权时调用doGetAuthenticationInfo(AuthenticationToken authenticationToken)方法,是在SecurityUtils.getSubject.isPermitted时调用 , 而我们平常用注解@RequirePermissions()时则会调用SecurityUtils.getSubject().isPermitted() , 最终可以调用到自定义Realm中重写的doGetAuthenticationInfo(AuthenticationToken authenticationToken)方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shiro框架是一个功能强大的Java安全框架,可以用于认证授权。下面是使用Shiro进行认证授权的基本步骤: 1. 添加Shiro依赖:在你的项目中添加Shiro的相关依赖,可以通过Maven或者Gradle进行引入。 2. 配置Shiro:创建一个Shiro的配置文件(通常是一个ini文件),配置Shiro的一些基本信息,例如Realm、Session管理等。你可以根据你的需求进行配置。 3. 创建RealmRealm是Shiro用来进行认证授权的核心组件。你需要实现一个自定义Realm来实现用户认证授权逻辑。在Realm中,你可以通过重写相应的方法来实现自定义认证授权逻辑,例如验证用户名密码、查询用户角色和权限等。 4. 认证:在用户登录时,你可以通过调用Shiro提供的Subject.login方法来进行认证。该方法会将用户提交的用户名和密码传递给你自定义Realm进行验证。如果验证通过,Shiro会将用户信息保存在Session中。 5. 授权:在用户进行操作时,你可以通过调用Shiro提供的Subject.hasRole和Subject.isPermitted方法来进行授权判断。这些方法会根据你在Realm中配置的角色和权限信息进行判断,决定用户是否具有相应的角色或权限。 以上是使用Shiro进行认证授权的基本步骤,你可以根据具体的需求进行定制和扩展。在实际应用中,你还可以通过Shiro提供的其他功能,如RememberMe、Session管理等来增强安全性和用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值