shiro认证及授权的执行流程分析及图解(一)

一,认证

1、先建两个class文件

①、一个写 AuthRealm (授权与认证方法,并继承) extends AuthorizingRealm  获取其默认方法doGetAuthorizationInfo(授权方法) doGetAuthenticationInfo(认证方法) 

②、一个写PasswordMatcher(密码验证器,并继承) extends SimpleCredentialsMatcher获取默认方法doCredentialsMatch 

2、在Action的登录方法中

         @Action("loginAction_login")//重页面跳转过来的路径名
         public String login() throws Exception {
             //判断用户名是不是为空,如果是说明没有登录,跳转到用户登录页面
             if(UtilFuns.isEmpty(username)){
                    return "login";
             }

//1.SecurityUtils:是shiro的一个工具类。通过SecurityUtils获取getSubject 得到一个返回值

Subject subject = SecurityUtils.getSubject();

//3.根据逻辑2。new一个 UsernamePasswordToken,并传上用户名及密码。把返回值传给登入作为条件。

 UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
//2. subject的返回值里有两个方法logout:登出 login:登入。这里我们使用登录方法,通过方法我们可以看到需要一个返回值:AuthenticationToken的类型,又因为AuthenticationToken  是一个接口,
//所以我们使用他下面的UsernamePasswordToken 的实现类来写(ps:查看方法:Ctrl+t),
subject.login(token);//当调用subject的登入方法时,会跳转到认证的方法上。。。。。。。。。。

//4.在证方法中subject已经把获取到了用户,所以我们用subject.getPrincipal 可以获取到登录的用户,Principal:他是在认证方法中的principal:主要对象(登录的用户,详情看认证方法return的哪一步注释)

User user = (User) subject.getPrincipal();

//5.把user用户数据通过Session.put放在session值栈中。SysConstant.CURRENT_USER_INFO:是一个返回的值,在jsp页面中可以接收到,也可以直接写一个字符串让页面接收,返回的数据可以在页面做回显等功能

                    session.put(SysConstant.CURRENT_USER_INFO, user);
                    return SUCCESS;//以上全部都过了后,让其访问页面数据
             } catch (Exception e) {
                    e.printStackTrace();
                    super.put("errorInfo", "您的用户名或密码错误"); //登录页面的错误信息提示
                    return "login";
             }
       }
         //退出
       @Action("loginAction_logout")
       public String logout(){
             session.remove(SysConstant.CURRENT_USER_INFO);       //删除session
             SecurityUtils.getSubject().logout();   //调用登出方法
             return "logout";
       }

3.在认证方法中的doGetAuthenticationInfo

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
             System.out.println("调用认证方法");
             //先将arg0强转为UsernamePasswordToken类型
             UsernamePasswordToken token =(UsernamePasswordToken)arg0;
             //通过token获取到用户名
             final String username = token.getUsername();
             //把username 设为查询条件,查询数据库是否有这个用户名
             Specification<User> spec = new Specification<User>() {   
             @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
             return cb.equal(root.get("username").as(String.class),username);
             }
             };
             //把spec 条件放到 查询中查询数据  查询用户名
             List<User> find = userServiceimpl.find(spec); 
              //判断返回的结果不为空,及返回的数不小于0
             if(find!=null&& find.size()>0){ 
             //通过索引获取到返回值的第一个数中的数据
             User user = find.get(0); 
             //这里如果上面的都成立后会return到密码校验哪里去
              return new SimpleAuthenticationInfo(user, user.getPassword(), getName()) ;
  /*   SimpleAuthenticationInfo:是doGetAuthenticationInfo的一个实现类,因为doGetAuthenticationInfo 是一个接口不能直接new  
       把返回值添加到条件中 //principal:主要对象(登录的用户) , credentials:密码 ,realm的名字可以通过getName获取类名作为区分 */
             }
             return null;
       }

4.在密码检验中PasswordMatcher  

public class PasswordMatcher extends SimpleCredentialsMatcher {
            //先实现一个接口SimpleCredentialsMatcher  获取doCredentialsMatch的内部方法       
            @Override
            public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
                System.out.println("调用了密码对比器");
                UsernamePasswordToken utoken = (UsernamePasswordToken) token;
            /*[把AuthenticationToken 中的token 装换为AuthenticationToken 中的(ps:查看方法Ctrl+t)
            UsernamePasswordToken实现类 因为AuthenticationToken 是接口不能new数据],*/
                String pwd = new String(utoken.getPassword());
                //通过utoken 获取用户密码,并转换成String类型,注意这里的转换不能强转,要用new的方法
                // source:要加密的内容   salt:增加复杂度的内容  哈希次数:2
                Md5Hash md5Hash = new Md5Hash(pwd, utoken.getUsername(), 2);
                //调用Md5加密 为输入的数据加密
                String credentials = (String) info.getCredentials();
                //通过AuthenticationInfo 的info 查询数据库的密码 装换成String类型
                //对比用户输入的数据和数据库密码是否一致。return走,返回到Action方法中。
                return equals(md5Hash.toString(), credentials); 

       }
}

5、执行流程图解

二、授权

1.在授权方法中

	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        System.out.println("进入授权方法");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        User user = (User) arg0.getPrimaryPrincipal();
       //获取用户的角色及角色的模块
        //通过用户查到用户角色
        Set<Role> roles = user.getRoles();  
        //遍历数据
        for (Role role : roles) {
            Set<Module> modules = role.getModules();//通过用户查到用户模块
            for (Module module : modules) {
	//通过用户模块查到模块名并添加到info中。也就是说把查到的模块显示到页面中,没有的就代表没授权
                info.addStringPermission(module.getCpermission());
            }
        }
        return info;  //返回到jsp页面中
    }

2.jsp页面

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>

 //要想使用shiro的标签就一点要导数据标签

<shiro:hasPermission name="部门管理"> //shiro:hasPermission:拥有权限的资源  name:权限访问的名字,如上:当该用户里有部门管理权限时,才会让其看到部门管理这个按钮      

<shiro:hasPermission>
      <li>
        <a href="${ctx}/sysadmin/deptAction_list" onclick="linkHighlighted(this)" 
          target="main" id="aa_1">部门管理
       </a>
    </li>
</shiro:hasPermission>

3以上方法可以使用户看不到自己没有权限的数据,但是如果用户自己写链接还是能访问到数据的,

解决方法一(推荐):过滤器链的权限配置方式:

<value>
                /index.jsp* = anon
                /home* = anon
                /sysadmin/login/login.jsp* = anon
                /sysadmin/login/loginAction_logout* = anon
                /login* = anon
                /logout* = anon
                /components/** = anon
                /css/** = anon
                /img/** = anon
                /js/** = anon
                /plugins/** = anon
                /images/** = anon
                /js/** = anon
                /make/** = anon
                /skin/** = anon
                /stat/** = anon
                /ufiles/** = anon
                /validator/** = anon
                /resource/** = anon
               //在这里进行配置,下面的以上为 /sysadmin/deptAction_*路径下的所有方法都必须要有部门管理权限才可以进入访问,(ps:在写方法时,建议写一个在这里就配置一个,避免混淆了)
                /sysadmin/deptAction_* = perms["部门管理"]
                /** = authc
                /*.* = authc
            </value>

解决方法二(推荐):注解的方式:

//这里代表的时要走这个方法模块中就得有角色管理这个模块,没有就拒绝访问

   @RequiresPermissions(value="角色管理")
    public Page<Role> findPage(Specification<Role> spec, Pageable pageable) {
        return roleDao.findAll(spec, pageable);
    }

文章转载自:https://blog.csdn.net/weixin_41716049/article/details/84336669

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值