shiro学习(二) Realm

 
   
realm是一个shiro提供的接口用中文翻译是'领域',当我们的表单post请求进行登录验证时,就会到自定义(之前还会有一个Filter)的这个realm,在这个类 我们查询出用户的信息(包括权限信息),然后交给credentialsMatcher进行验证(凭证匹配器,下一篇会讲到)

 
    
 
package com.hzq.system.service.realm;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
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.stereotype.Service;
 
import com.hzq.system.entity.ShiroUser;
import com.hzq.system.entity.SysPermission;
import com.hzq.system.entity.SysRole;
import com.hzq.system.entity.SysUser;
import com.hzq.system.service.PermissionService;
import com.hzq.system.service.SysRoleService;
import com.hzq.system.service.SysUserService;
import com.hzq.system.util.PermissionUtil;
@Service
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private SysUserService userService;
@Autowired
private SysRoleService roleService;
@Autowired
private PermissionService permissionService;
@Override
public void setName(String name) {
super.setName("shiroRealm");
}
/**
* 认证回调函数,登录时调用.
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
// token是用户输入的用户名和密码
// 从token中取出用户名
String username = (String) authcToken.getPrincipal();
SysUser sysUser=userService.findUserByUsername(username);
if(sysUser==null){
return null;
}
if("1".equals(sysUser.getState())){
throw new LockedAccountException();
}
ShiroUser shiroUser=new ShiroUser(sysUser.getId(),sysUser.getUsername(), sysUser.getName(),sysUser.getPhone());
List<SysRole> roles=roleService.getRolesByUserId(sysUser.getId());
shiroUser.setRoles(roles);
List<SysPermission> permissions=new ArrayList<SysPermission>();
if(roles.size()!=0){
permissions=permissionService.getPermissionBySysRoles(roles);
}
shiroUser.setMenus(PermissionUtil.getMenus(permissions));
shiroUser.setPermissions(PermissionUtil.getPermissions(permissions));
String salt=sysUser.getSalt();
return new SimpleAuthenticationInfo(shiroUser,sysUser.getPassword(),ByteSource.Util.bytes(salt),getName());
}
 
/**
* 授权查询回调函数, 进行授权但缓存中无用户的授权信息时调用.
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// for (SysRole role : shiroUser.getRoles()) {
// //基于Role的权限信息(与代码绑定太多)
// info.addRole(role.getAuthorityname());
// }
for (SysPermission permission : shiroUser.getPermissions()) {
//基于Permission的权限信息
String auth=permission.getAuth();
if(auth!=null){
info.addStringPermission(auth.trim());
}
}
return info;
};
 
 
}
 


我们需要自定义一个实现  AuthorizingRealm ,当我们的登录表单被过滤器拦截之后,便会进入这个类进行认证, 这个类主要有两个方法
 
    
(1): doGetAuthenticationInfo 
    
  shiro是利用过滤器进行登录拦截的,当我们没登录时访问任何页面,都会跳到配置文件定义的ShiroFilter里面的loginUrl,然后在进行表单提交时,跳转到这里进行验证(其实之前还会有一个登录表单的过滤器,下下篇文章会介绍),若验证失败(这个方法返回null 或者抛出异常)则会跳到LoginUrl对应的Action里面(这个Action并不处理登录成功,切记只有登录失败才会跳转)
    这个主要方法是查找用户名密码信息(在此我也查出了用户所具有的权限信息,这样下面需要认证时就不用再从数据库查了)
    详细介绍:
    参数authcToken 储存了用户表单提交过来的用户名和密码(也包括了是否记住密码,我这边没有用)
    返回值 SimpleAuthenticationInfo 
            第一个参数是Object,为了储存更多的信息,我自定义了一个ShiroUser来存储 在方法(2)中通过 
     ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal() 可以得到ShiroUser
    sysUser.getState()是bean的一个字段,若为1则用户禁用,此时抛出异常(这个异常是shiro内部自定义的)前面有提到,抛出异常则会跳转到LoginUrl对应的Action,看下那个Action
 
    
      /**
     * 此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping(value="login",method=RequestMethod.POST)
    public String index(HttpServletRequest request) throws Exception{
        
        //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
                String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
                //根据shiro返回的异常类路径判断,抛出指定异常信息
                if(exceptionClassName!=null){
                    if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
//                        request.setAttribute(IndexErrorKey, "账号不存在");
                        //避免被恶意扫描账号库
                        request.setAttribute(IndexErrorKey, "用户名/密码错误");
                    } else if (IncorrectCredentialsException.class.getName().equals(
                            exceptionClassName)) {
                        request.setAttribute(IndexErrorKey, "用户名/密码错误");
                    } else if("validataCodeError".equals(exceptionClassName)){
                        request.setAttribute(IndexErrorKey, "验证码错误");
                    }  else if("usernameValidError".equals(exceptionClassName)) {
                        request.setAttribute(IndexErrorKey, "用户名长度不合法");
                    }else if("passwordValidError".equals(exceptionClassName)){
                        request.setAttribute(IndexErrorKey, "密码长度不合法");
                    }else if(ExcessiveAttemptsException.class.getName().equals(exceptionClassName)){
                        request.setAttribute(IndexErrorKey, "验证失败次数过多,5分钟后重试");
                    } else if(LockedAccountException.class.getName().equals(exceptionClassName)){
                        request.setAttribute(IndexErrorKey, "账号被锁定,请联系管理员");
                    }else if (AuthenticationException.class.getName().equals(exceptionClassName)){
                        request.setAttribute(IndexErrorKey, "用户名或密码错误");
                    }else
                        request.setAttribute(IndexErrorKey, "未知错误");
                    }
                    //如果认证错误 返回login页面(显示错误信息)
                    return "login";
                }
                //登陆失败回退首页(会自动跳转到登录页面)
                //若一个已经登录的用户发送get请求/login实际上无论怎么输入表单数据都会跳转到index页面 
                return "redirect:/";
    }
  String  exceptionClassName  =   ( String )  request . getAttribute ( "shiroLoginFailure" );
这段代码可能没看源码的会有点迷糊,其实shiro框架内部在进行验证失败后,把这个ExceptionName放到request,shiroLoginFailure中,然后再跳转到这个页面,Shiro几个常用的Exception为DisAbleAccountException(账号被禁用),LockedAccountException(账号被锁定) ExcessiveAttemptsException(登录次数过多) ExpiredCredentialsException(凭证过期),我这边异常种类多,(其实之前还会有一个登录表单的过滤器,下下篇会讲到)我只是在验证之前对于验证失败的部分做了这个操作:request.setAttribute(' shiroLoginFailure ',' validataCodeError ') 这样就实现了 验证码等一些拦截,在这个方法认证失败后,我把错误信息放在request域中,并且返回到Login页面进行显示

 
     
(2): doGetAuthorizationInfo
    这是权限认证,前面在登录认证时,我已经把权限信息放在ShiroUser里面了,我们只要取出其中的permission按照我这样返回就好了,我这边数据库存的是 类似于 "user:query"表示用户查询的权限,然后在方法上进行拦截,jsp页面用shiro标签进行过滤即可

你可能感兴趣的:(shiro)