@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 类的运作我不清楚 ?
自己实现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中读取出来
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。