资讯专栏INFORMATION COLUMN

我是这样用shiro的

nanchen2251 / 2972人阅读

摘要:我这里为了简化只写了用户名密码参数。如下代码可以退出当前用户,这样我们再请求登录的时候就会继续安全认证了。当我们登录成功后,我们用是角色但是没有权限的用户访问是可以请求成功,但是访问是会跳转到的配置文件的对应的地址,表示权限不足不可访问。

小述

我在shiro学习上花费了一些时间,shiro的资料网上一大推,之前自己学习的知识点一直记录在有道云笔记上,有道云有自己的好处 那就是没有网络的时候依然可以记录一些东西,但是弊端就是不能与大家一起分享和讨论,最后还是选择在segmentFault,一上来发现自己声望值是负数,有点小悲伤啊,以后学习到东西后自己会在这里写写记记,一是归纳梳理知识且自己记性不好,方便以后自我回忆,二是希望能和大家讨论,我有不对的地方希望能有大神指点。废话不多说,写一下我最近学习的shiro的用法。

正文

大体的登录认证流程如上图,当进入登录页面后,会先进入通过shiroFilter安全认证过滤器,然后读取数据库信息来进行登录和授权的认证,这一部分是交给realm来进行的,当认证成功后会跳转到successURL对应的地址中去如果失败会跳转到loginUrl中。图上的的${adminPath}只是从配置文件properties中读出配置参数罢了,可以把它看成/a。

web.xml配置
    
    
        shiroFilter
        org.springframework.web.filter.DelegatingFilterProxy
        
            targetFilterLifecycle
            true
        
    
    
        shiroFilter
        /*
    

web.xml基本上是固定模板,/*拦截了所有的请求,但是记住下shiroFilter这个名字。

spring-context-shiro.xml 配置
Shiro Configuration

    
    
    
    
    
        
            
                /static/** = anon
                /userfiles/** = anon
                ${adminPath}/login = authc
                ${adminPath}/logout = logout
                ${adminPath}/sys/** = roles[sys]
                ${adminPath}/cms/** = perms[cms:view]
                ${adminPath}/** = user
            
        
    
    
    
    
         
        
        
        
        
            
                
            
        
        
            
        
    
    
    
    
        
        
    
    
    
    
    
    
    
        
    
    
        
    

XML配置文件中很多也是固定不变的,官网文档也有介绍,anon表示请求对应的路径不认证,authc表示请求对应的路径需要登录认证,roles[sys]表示请求对应的路径需要角色为sys的才允许,perms[cms:view]表示请求对应的路径需要有cms:view权限的才允许。这里的安全认证过滤器的名字要与web.xml中的shiroFilter一样。

表示登录失败后会跳转到对应请求

表示登录成功后会跳转的请求

表示访问了无权访问的链接后跳转的请求。

 

表示 authc这个会通过FormAuthenticationFilter这个类来验证

这是我们自定义的realm来认证登录和授权信息的

实验的目录结构,如上图

重写FormAuthenticationFilter类
@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter{
    
    public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
    
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;

    @Override
    protected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {
        System.out.println("-------------进入创建token方法-------------");
        // TODO Auto-generated method stub
        String username = getUsername(request);
        String password = getPassword(request);
        if(password == null){
            password = "";
        }
        String captcha = getCapcha(request);
        System.out.println("-------------出创建token方法-------------");
        return new UsernamePasswordToken(username,password,captcha);
    }
    
    
    public String getCapcha(ServletRequest request){
        return WebUtils.getCleanParam(request, captchaParam);
    }
    
    public String getSuccessUrl(){
        return super.getSuccessUrl();
    }
    
    @Override
    protected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws Exception {
        // TODO Auto-generated method stub、
        System.out.println("-------------issueSuccessRedirect-------------");
        WebUtils.issueRedirect(request, response, getSuccessUrl());
    }


    @Override
    protected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        // TODO Auto-generated method stub
        System.out.println("-------------onLoginFailure-------------");
        String className = e.getClass().getName();
        String message = "";
        System.out.println("=========e.getCLass().getName()===========:"+className);
        if(IncorrectCredentialsException.class.getName().equals(className)
                || UnknownAccountException.class.getName().equals(className)){
            message = "用户名或密码错误";
        }else{
            message = "系统出现点问题,请稍后再试!";
            e.printStackTrace(); // 输出到控制台
        }
        request.setAttribute("message",message);
        return true;
    }

}

当我们输入完用户密码后需要进入一个 shiroFilter 安全认证过滤器,这里为什么需要重写了呢,其实本质是shiro已经给我们提供了相应的 FormAuthenticationFilter 类,可以在其中根据用户输入的信息创建 token 来供以后流程的认证,但是如果我们还想加入一些其他的东西来一起创建这个 token,比如说我们通过用户名、密码、验证码、是否记住我、是否是手机端登录等等信息来一起组成一个token的时候,这个时候我们就可以重写 FormAuthenticationFilter 中的一些方法来实现了。

WebUtils.getCleanParam(request, captchaParam) 是 shiro 给我们提供的封装,其实就是 request.getParameter(paramName) 的一个封装罢了,我们可以通过它来获取前台输入的参数。

重写UsernamePasswordToken类
public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken{
    
    private static final long serialVersionUID = 1L;
    
    private String captcha;
    
    public UsernamePasswordToken(String username,String password){
        super(username,password);
    }

    public UsernamePasswordToken() {
        super();
    }
    
    public UsernamePasswordToken(String username,String password,String captcha){
        super(username,password);
        this.captcha = captcha;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }
    
}

通过重写UsernamePasswordToken,目的是来根据开发者想创建token的参数来构造出一个新的UsernamePasswordToken以供使用。我这里为了简化只写了用户名密码参数。

此时此刻我们就有了token,下面开始我们的认证啦~

通过自定义的 Realm 来认证授权
@Service
public class SystemAuthorizingRealm extends AuthorizingRealm{
    
    @Autowired
    private SystemService systemService;
    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        // TODO Auto-generated method stub
        System.out.println("-------------进入登录认证方法-------------");
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        User user = systemService.getUserByName(username);
        System.out.println("-------1----getName()--------:"+getName());
        if(user != null){
            System.out.println("-------2----getName()--------:"+getName());
            System.out.println("-------------登录认证结束--返回SimpleAuthenticationInfo-------------");
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        }else{
            System.out.println("------------登录认证结束--返回null------------");
            return null;
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // TODO Auto-generated method stub
        System.out.println("-------------进入授权认证方法-------------");
//        Principal principal = (Principal) getAvailablePrincipal(principals);
        String name = (String) principals.getPrimaryPrincipal();
//        User user = systemService.getUserByName(principal.getName());
        User user = systemService.getUserByName(name);
        if(user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            List Roles = systemService.getRoleByUserId(user.getId());
            for(Role r:Roles){
                System.out.println("role---> "+r.getRole());
                info.addRole(r.getRole());
                List Perms = systemService.getPermByRoleId(r.getId());
                if(Perms != null && Perms.size() >0 ){
                    for(Perm p:Perms){
                        System.out.println("perm---> "+p.getPermission());
                        info.addStringPermission(p.getPermission());
                    }
                }
            }
            System.out.println("-----------授权认证结束--返回info------------");
            return info;
        }else{
            System.out.println("-----------授权认证结束--返回null------------");
            return null;
        }
    }
}

在 realm 中我们需要继承 AuthorizingRealm,并重写两个方法:

doGetAuthenticationInfo 方法:用户身份认证方法,根据参数返回
SimpleAuthenticationInfo,如果登录成功则跳转到xml配置文件中的 successUrl 地址,如果登录失败则跳转到 loginUrl 地址;

doGetAuthorizationInfo 方法:身份权限认证,利用了 SimpleAuthorizationInfo,把角色和权限都给予 SimpleAuthorizationInfo 来进行授权;在是 shiro 中如果访问了无权访问的地址,则会跳转到 xml 配置文件中
unauthorizedUrl 对应的地址。

到这里基本上shiro就完毕了,当我们登录时候的顺序:

上图为登录成功的流程

上图为登录失败的流程

这时候 shiro 已经记住了用户的信息,当再请求路径的时候就不会继续验证了,在此输入请求拦连接的时候就会直接去找对应的 action 而不会再进入安全过滤器,所有我们想在此登录需要退出当前用户才行。如下代码可以退出当前用户,这样我们再请求登录的时候就会继续安全认证了。

SecurityUtils.getSubject().logout();

当我们登录成功后,我们用是sys角色但是没有 perms[cms:view] 权限 的用户访问 a/sys 是可以请求成功,但是访问 a/cms 是会跳转到 shiro 的 xml 配置文件的unauthorizedUrl 对应的地址,表示权限不足不可访问。

上图为授权成功,用户有该请求权限,跳转到对应 action 中

上图为授权失败,跳转到 unauthorized.jsp 页面,提示无权访问

以后会有新的内容会加已补充,比如shiro的标签和shiro和ehcache的整合等等。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70053.html

相关文章

  • Shiro权限管理(一)——介绍

    摘要:大家好,今天给大家分享一个权限管理的框架的,说实话本来我是准备看的,毕竟是家族的框架,和整合更加容易一些。官方给出的介绍是是一个强大且易用的安全框架执行身份验证授权密码学和会话管理。由此可知,的主要功能是认证授权加密密和会话管理。 showImg(https://segmentfault.com/img/bV1BsT?w=1726&h=256); 大家好,今天给大家分享一个权限管理的框...

    liuhh 评论0 收藏0
  • Shiro权限管理(一)——介绍

    摘要:大家好,今天给大家分享一个权限管理的框架的,说实话本来我是准备看的,毕竟是家族的框架,和整合更加容易一些。官方给出的介绍是是一个强大且易用的安全框架执行身份验证授权密码学和会话管理。由此可知,的主要功能是认证授权加密密和会话管理。 showImg(https://segmentfault.com/img/bV1BsT?w=1726&h=256); 大家好,今天给大家分享一个权限管理的框...

    王伟廷 评论0 收藏0
  • 【Java EE】从零开始写项目【总结】

    摘要:目前该功能并未完善,敬请期待。反正每次都会有新的东西补充上去一开始我本来想做的是可以使用微信登陆,也可以使用账户邮箱登陆,也可以使用短信登陆的。后来发现微信登陆要企业认证,做不了。 从零开发项目概述 最近这一直在复习数据结构和算法,也就是前面发出去的排序算法八大基础排序总结,Java实现单向链表,栈和队列就是这么简单,十道简单算法题等等... 被虐得不要不要的,即使是非常简单有时候绕半...

    imtianx 评论0 收藏0
  • Spring Security

    摘要:框架具有轻便,开源的优点,所以本译见构建用户管理微服务五使用令牌和来实现身份验证往期译见系列文章在账号分享中持续连载,敬请查看在往期译见系列的文章中,我们已经建立了业务逻辑数据访问层和前端控制器但是忽略了对身份进行验证。 重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 评论0 收藏0
  • Shiro统一认证授权

    摘要:的统一认证授权是下面的一个简单,易用的权限框架,对于单体应用来讲,完全能够极好的,快速的满足权限的需求,所以一般在做项目的时候,都会成为开发者的首选。 Shiro的统一认证授权 Shiro是Apache下面的一个简单,易用的Java权限框架,对于单体应用来讲,Shiro完全能够极好的,快速的满足权限的需求,所以一般在做项目的时候,Shiro都会成为开发者的首选。 可是,如果你需要做第二...

    cocopeak 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<