Shiro自定义Token

Shiro自定义Token

1. 序言

  • 用户登录检验需要校验多个要素,不只有用户名和密码
  • 比如需要验证公司ID,主机IP,等等

2. shiro中Token的结构

Shiro中Token的继承结构图:

Shiro中Token继承结构图

  • AuthenticationToken接口类只包含获取用户的身份信息和凭证信息两个方法。

    • Object getPrincipal();
      
    • Object getCredentials();
      
  • HostAuthenticationToken接口类继承自AuthenticationToken接口,额外添加了获取主机地址方法。

    • String getHost();
      
  • RememberMeAuthenticationToken接口类继承自AuthenticationToken接口,额外添加了是否记住用户的布尔方法

    • boolean isRememberMe();
      

!所以,若想重写Token,只需要实现AuthenticationToken接口,然后添加自己需要添加的字段就可以了。

3. 自定义Token

/**
 * @Author: Hjx
 * @Date: 2021/8/10 11:17
 */
public class JWTToken implements AuthenticationToken {
    /**
     * 公司ID
     */
    String companyId;
    /**
     * 用户名称
     */
    String userName;
    /**
     * 用户密码
     */
    String userPass;
    
    public JWTToken(String companyId, String userName, String userPass) {
        this.companyId = companyId;
        this.userName = userName;
        this.userPass = userPass;
    }

    public void setCompanyId(String companyId) {
        this.companyId = companyId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setUserPass(String userPass) {
        this.userPass = userPass;
    }

    public String getCompanyId() {
        return companyId;
    }

    public String getUserName() {
        return userName;
    }

    public String getUserPass() {
        return userPass;
    }

    @Override
    public Object getPrincipal() {
        return getUserName();
    }

    @Override
    public Object getCredentials() {
        return getUserPass();
    }
}

4. 重写supports方法

因为是自己定义的Token,shiro无法识别,需要修改Realm中的supports方法,使 shiro 支持自定义token。

/**
 * 重写supports方法,使 Shiro 能够识别自定义的 Token
 * @param token
 * @return
 */
@Override
public boolean supports(AuthenticationToken token) {
    return token instanceof JWTToken;
}

5. 修改认证方法doGetAuthenticationInfo

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // 从 token 中获取用户输入的身份凭证
    JWTToken jwtToken = (JWTToken) token;
    String userName = (String)jwtToken.getPrincipal();

    // 模拟从数据库查询出的用户名和密码
    String name = "hjx";
    String pass = "1a0f1aac48cf23ffb080673fd60b5d2d";
    // 盐值
    String credentialsSalt = "hjx";
    String companyId = "100000";

    // 对companyId进行判断
    if (!companyId.equals(jwtToken.getCompanyId())){
        try {
            throw new Exception("公司ID不符!!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    if (name.equals(userName)){
        /*
            SimpleAuthenticationInfo 是 AuthenticationInfo 的实现类
            参数1:用户名
            参数2:md5加密后的密码
            参数3:盐值,类型为ByteSource,需要进行格式转换
            参数4:自定义realm的名称
         */
        return new SimpleAuthenticationInfo(name, pass, ByteSource.Util.bytes(credentialsSalt), this.getName());
    }
    return null;
}

6. 修改登录验证

/**
 * @Author: Hjx
 * @Date: 2021/8/10 11:43
 */
public class TestJWTAuthenticator {
    public static void main(String[] args) {
        // 创建SecurityManager安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        // 给SecurityManager设置自定义Realm
        JWTRealm jwtRealm = new JWTRealm();

        // 设置md5 凭证适配器
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 设置加密方式
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 设置散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        jwtRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        defaultSecurityManager.setRealm(jwtRealm);
        // 给安全工具类SecurityUtils 设置 SecurityManager
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        // 通过安全工具类SecurityUtils获取 主体subject
        Subject subject = SecurityUtils.getSubject();

        // 创建Token,模拟接收前端用户输入的用户名和密码
        String userName = "hjx";
        String userPass = "123";
        String companyId = "100000";
        JWTToken jwtToken = new JWTToken(companyId,userName,userPass);

        try {
            System.out.println("认证状态 : "+subject.isAuthenticated());
            subject.login(jwtToken);
            System.out.println("认证状态 : "+subject.isAuthenticated());
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

7. 附完整的自定义Realm

/**
 * @Author: Hjx
 * @Date: 2021/8/10 11:25
 */
public class JWTRealm extends AuthorizingRealm {

    /**
     * 重写supports方法,使 Shiro 能够识别自定义的 Token
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        /*
            PrincipalCollection 身份的集合
            一个主体可以有多个身份,但是只有一个主身份
         */
        // 获取主体的主身份
        String principal = (String) principals.getPrimaryPrincipal();
        System.out.println("主身份信息"+principal);

        // 模拟根据用户的身份信息 从数据库中查询其角色信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");

        // 模拟从数据库查询权限信息,赋值给某个对象
        simpleAuthorizationInfo.addStringPermission("user:*:01");
        simpleAuthorizationInfo.addStringPermission("product:create:01");

        return simpleAuthorizationInfo;
    }


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从 token 中获取用户输入的身份凭证
        JWTToken jwtToken = (JWTToken) token;
        String userName = (String)jwtToken.getPrincipal();

        // 模拟从数据库查询出的用户名和密码
        String name = "hjx";
        String pass = "1a0f1aac48cf23ffb080673fd60b5d2d";
        // 盐值
        String credentialsSalt = "hjx";
        String companyId = "100000";

        // 对companyId进行判断
        if (!companyId.equals(jwtToken.getCompanyId())){
            try {
                throw new Exception("公司ID不符!!!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (name.equals(userName)){
            /*
                SimpleAuthenticationInfo 是 AuthenticationInfo 的实现类
                参数1:用户名
                参数2:md5加密后的密码
                参数3:盐值,类型为ByteSource,需要进行格式转换
                参数4:自定义realm的名称
             */
            return new SimpleAuthenticationInfo(name, pass, ByteSource.Util.bytes(credentialsSalt), this.getName());
        }
        return null;
    }
}

代码及资料地址:hjx: 知道的越多,越发觉自己的无知 (gitee.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值