开发者社区> 问答> 正文

shiro 认证问题报错 

@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";
}
如上所示,这是一个 shiro 登录的 spring mvc 接口
protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
	System.out.println("[SecondReaml] doGetAuthenticationInfo");
		
	//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 = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
	if("admin".equals(username)){
		credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
	}else if("user".equals(username)){
		credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
	}
		
	//3). realmName: 当前 realm 对象的 name. 调用父类的 getName() 方法即可
	String realmName = getName();
	//4). 盐值. 
	ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		
	SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
	info = new SimpleAuthenticationInfo("secondRealmName", credentials, credentialsSalt, realmName);
	return info;
}
这个是自定义认证方法,在spring 配置文件中配置的。         我始终有一个疑惑,SimpleAuthenticationInfo  到底是怎么能够认证的用户是否存在,用户密码是否正确 ?在不使用spring 的时候,会有一个 shiro.ini 配置文件,这个里面有用户名密码等信息,但是在使用了 spring 这个配置文件就不需要了,然后我们自定义认证类,在自定义认证类中认证,到底是哪一步跟数据库的用户关联的?SimpleAuthenticationInfo 做认证的信息是在哪里 ?
String username = upToken.getUsername();
        这个获取的是用户登录的时候输入的用户名密码 ? 还是说我们所判断的用户不存在、密码错误、用户锁定 ,这些信息都是自己使用
throw new LockedAccountException("用户被锁定");
这种方式配判断的 ?自己手动判断 ?然后抛出异常 ?         我感觉我上述的所有的疑惑好像都是不清楚 SimpleAuthenticationInfo 来的运作。还是 AuthenticatingRealm 类的运作我不清楚 ?

展开
收起
kun坤 2020-06-03 11:34:14 574 0
1 条回答
写回答
取消 提交回答
  • 自己实现AuthorizingRealm类,然后验证用户和权限,这里需要自己实现功能,跟数据库交互的。

    /** * 自定义Realm 处理登录 权限 * * @author ruoyi */ public class UserRealm extends AuthorizingRealm { private static final Logger log = LoggerFactory.getLogger(UserRealm.class);

    @Autowired
    private IMenuService menuService;
    
    @Autowired
    private IRoleService roleService;
    
    @Autowired
    private LoginService loginService;
    
    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
    {
        Long userId = ShiroUtils.getUserId();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 角色加入AuthorizationInfo认证对象
        info.setRoles(roleService.selectRoleKeys(userId));
        // 权限加入AuthorizationInfo认证对象
        info.setStringPermissions(menuService.selectPermsByUserId(userId));
        return info;
    }
    
    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
    {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        String password = "";
        if (upToken.getPassword() != null)
        {
            password = new String(upToken.getPassword());
        }
    
        User user = null;
        try
        {
            user = loginService.login(username, password);
        }
        catch (UserNotExistsException e)
        {
            throw new UnknownAccountException(e.getMessage(), e);
        }
        catch (UserPasswordNotMatchException e)
        {
            throw new IncorrectCredentialsException(e.getMessage(), e);
        }
        catch (UserPasswordRetryLimitExceedException e)
        {
            throw new ExcessiveAttemptsException(e.getMessage(), e);
        }
        catch (UserBlockedException e)
        {
            throw new LockedAccountException(e.getMessage(), e);
        }
        catch (RoleBlockedException e)
        {
            throw new LockedAccountException(e.getMessage(), e);
        }
        catch (Exception e)
        {
            log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
            throw new AuthenticationException(e.getMessage(), e);
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }
    

    }

     ######手动判断 抛出异常。 自己比对密码######你怕是需要好好了解下shiro。 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);
      SimpleAuthenticationInfo:
        /* * Constructor that takes in a single 'primary' principal of the account, its corresponding hashed credentials, * the salt used to hash the credentials, and the name of the realm to associate with the principals. * <p/> * This is a convenience constructor and will construct a {@link PrincipalCollection PrincipalCollection} based * on the <code>principal</code> and <code>realmName</code> argument. * * @param principal         the 'primary' principal associated with the specified realm. * @param hashedCredentials the hashed credentials that verify the given principal. * @param credentialsSalt   the salt used when hashing the given hashedCredentials * @param realmName         the realm from where the principal and credentials were acquired. * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher * @since 1.1 / public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName)
    综上,先获得登录的AuthenticationInfo(目标) 然后将其提供给CredentialsMatcher进行比对验证信息######问题拖得再久 也是要解决的 qaq ...  我想问的问题是 shiro 怎么跟数据库关联起来做验证的 ... 今天 low 了一眼 shiro 源码,发现了一个 JdbcRealm 这个类 贴一下我关注的内容
     /* * The default query used to retrieve account data for the user. */ protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";

    /**
     * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
     */
    protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
    
    /**
     * The default query used to retrieve the roles that apply to a user.
     */
    protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
    
    /**
     * The default query used to retrieve permissions that apply to a particular role.
     */
    protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
    
    可以发现 与数据库管理的认证 shiro 确实是已经实现了,默认的查询 users,user_roles,roles_permissions 确认的用户权限,可以继承这个类实现自己的认证需求,像 shiro 中  SaltAwareJdbcRealm 类就是继承的 JdbcRealm 实现的认证。######一切基于 AuthorizingRealm  
    doGetAuthenticationInfo  此方法是判断用户是否合法。你可以注入一个dao读取数据库来判断token登录是否合法。登录成功获取用户权限,我是写入session中。
    doGetAuthorizationInfo  此方法是取用户的权限列表。我是从session中读取出来
       
    2020-06-03 11:34:27
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
安全机制与User账户身份验证实战 立即下载
低代码开发师(初级)实战教程 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载