zoukankan      html  css  js  c++  java
  • shiro权限控制

    https://www.cnblogs.com/tohxyblog/p/8426230.html  分布式SHIRO

    第一部分 什么是Apache Shiro

     
     

    1、什么是 apache shiro :

     

    Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理

    如同 spring security 一样都是是一个权限安全框架,但是与Spring Security相比,在于他使用了和比较简洁易懂的认证和授权方式。

     

     

     

    2、Apache Shiro 的三大核心组件:

     

    1、Subject :当前用户的操作

    2、SecurityManager:用于管理所有的Subject

    3、Realms:用于进行权限信息的验证

     

    Subject:即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。

    SecurityManager:即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。

    Realms:Realms则是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realms来自定义的管理我们自己系统内部的权限规则。

     

     

     

    3、Authentication 和 Authorization

     

    在shiro的用户权限认证过程中其通过两个方法来实现:

    1、Authentication:是验证用户身份的过程。

    2、Authorization:是授权访问控制,用于对用户进行的操作进行人证授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

     

     

     

    4、其他组件:

     

    除了以上几个组件外,Shiro还有几个其他组件:

    1、SessionManager :Shiro为任何应用提供了一个会话编程范式。

    2、CacheManager :对Shiro的其他组件提供缓存支持。 

     

     

     

     

    第二部分 Apache Shiro 整合Spring的Web程序构建

     

    1、准备工具:

     

    持久层框架:Hibernate4  这边我使用了hibernate来对数据持久层进行操作

    控制显示层框架:SpringMVC 这边我使用了SpringMVC实际开发中也可以是其他框架

    数据库MySQL

    准备好所需要的jar放到项目中。

     

     

    2、创建数据库:

     
     

    首先需要四张表,分别为 user(用户)、role(角色)、permission(权限)、userRole(用户角色关系表)

    这边分别创建四张表的实体类,通过Hiberante的hibernate.hbm2ddl.auto属性的update 来自动生成数据表结构。

     

    /*** 
     * 用户表 
     *  
     * @author Swinglife 
     *  
     */  
    @Table(name = "t_user")  
    @Entity  
    public class User {  
      
        @Id  
        @GeneratedValue(strategy = GenerationType.AUTO)  
        Integer id;  
        /** 用户名 **/  
        String username;  
        /** 密码 **/  
        String password;  
        /** 是否删除 **/  
        Integer isDelete;  
        /** 创建时间 **/  
        Date createDate;  
        //多对多用户权限表  
        @OneToMany(mappedBy = "user",cascade=CascadeType.ALL)  
        List<UserRole> userRoles;  
      
    省略get set….  
      
    }  
    /**** 
     * 角色表 
     *  
     * @author Swinglife 
     *  
     */  
    @Entity  
    @Table(name = "t_role")  
    public class Role {  
        @Id  
        @GeneratedValue(strategy = GenerationType.AUTO)  
        Integer id;  
        /**角色名**/  
        String name;  
        /**角色说明**/  
        String description;  
      
      
    }  
    /**** 
     * 权限表 
     *  
     * @author Swinglife 
     *  
     */  
    @Entity  
    @Table(name = "t_permission")  
    public class Permission {  
      
        @Id  
        @GeneratedValue(strategy = GenerationType.AUTO)  
        Integer id;  
        /**token**/  
        String token;  
        /**资源url**/  
        String url;  
        /**权限说明**/  
        String description;  
        /**所属角色编号**/  
        Integer roleId;  
      
    }  
    /*** 
     * 用户角色表 
     *  
     * @author Swinglife 
     *  
     */  
    @Entity  
    @Table(name = "t_user_role")  
    public class UserRole {  
      
        @Id  
        @GeneratedValue(strategy = GenerationType.AUTO)  
        Integer id;  
      
        @ManyToOne(cascade = CascadeType.ALL)  
        @JoinColumn(name = "userId", unique = true)  
        User user;  
        @ManyToOne  
        @JoinColumn(name = "roleId", unique = true)  
        Role role;  
      
    }  

    3、编写操作用户业务的Service:

     

    @Service  
    public class AccountService {  
      
        /**** 
         * 通过用户名获取用户对象 
         *  
         * @param username 
         * @return 
         */  
        public User getUserByUserName(String username) {  
            User user = (User) dao.findObjectByHQL("FROM User WHERE username = ?", new Object[] { username });  
            return user;  
        }  
      
        /*** 
         * 通过用户名获取权限资源 
         *  
         * @param username 
         * @return 
         */  
        public List<String> getPermissionsByUserName(String username) {  
            System.out.println("调用");  
            User user = getUserByUserName(username);  
            if (user == null) {  
                return null;  
            }  
            List<String> list = new ArrayList<String>();  
            // System.out.println(user.getUserRoles().get(0).get);  
            for (UserRole userRole : user.getUserRoles()) {  
                Role role = userRole.getRole();  
                List<Permission> permissions = dao.findAllByHQL("FROM Permission WHERE roleId = ?", new Object[] { role.getId() });  
                for (Permission p : permissions) {  
                    list.add(p.getUrl());  
                }  
            }  
            return list;  
        }  
      
        // 公共的数据库访问接口  
        // 这里省略BaseDao dao的编写  
        @Autowired  
        private BaseDao dao;  
    } 

    4、编写shiro组件自定义Realm:

    package org.swinglife.shiro;  
      
    import java.util.List;  
      
    import org.apache.shiro.authc.AuthenticationException;  
    import org.apache.shiro.authc.AuthenticationInfo;  
    import org.apache.shiro.authc.AuthenticationToken;  
    import org.apache.shiro.authc.SimpleAuthenticationInfo;  
    import org.apache.shiro.authc.UsernamePasswordToken;  
    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.swinglife.model.User;  
    import org.swinglife.service.AccountService;  
      
    /**** 
     * 自定义Realm 
     *  
     * @author Swinglife 
     *  
     */  
    public class MyShiroRealm extends AuthorizingRealm {  
      
        /*** 
         * 获取授权信息 
         */  
        @Override  
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {  
            //根据自己系统规则的需要编写获取授权信息,这里为了快速入门只获取了用户对应角色的资源url信息  
            String username = (String) pc.fromRealm(getName()).iterator().next();  
            if (username != null) {  
                List<String> pers = accountService.getPermissionsByUserName(username);  
                if (pers != null && !pers.isEmpty()) {  
                    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
                    for (String each : pers) {  
                        //将权限资源添加到用户信息中  
                        info.addStringPermission(each);  
                    }  
                    return info;  
                }  
            }  
            return null;  
        }  
        /*** 
         * 获取认证信息 
         */  
        @Override  
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {  
            UsernamePasswordToken token = (UsernamePasswordToken) at;  
            // 通过表单接收的用户名  
            String username = token.getUsername();  
            if (username != null && !"".equals(username)) {  
                User user = accountService.getUserByUserName(username);  
                if (user != null) {  
                    return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());  
                }  
            }  
      
            return null;  
        }  
          
        /**用户的业务类**/  
        private AccountService accountService;  
          
        public AccountService getAccountService() {  
            return accountService;  
        }  
      
        public void setAccountService(AccountService accountService) {  
            this.accountService = accountService;  
        }  
      
    }  

    上述类继承了Shiro的AuthorizingRealm类 实现了AuthorizationInfo和AuthenticationInfo两个方法,用于获取用户权限和认证用户登录信息

     

    5、编写LoginController:

    package org.swinglife.controller;  
      
    import org.apache.shiro.SecurityUtils;  
    import org.apache.shiro.authc.UsernamePasswordToken;  
    import org.apache.shiro.subject.Subject;  
    import org.springframework.beans.factory.annotation.Autowired;  
    import org.springframework.stereotype.Controller;  
    import org.springframework.web.bind.annotation.RequestMapping;  
    import org.springframework.web.bind.annotation.RequestMethod;  
    import org.springframework.web.portlet.ModelAndView;  
    import org.swinglife.model.User;  
    import org.swinglife.service.AccountService;  
      
    /**** 
     * 用户登录Controller 
     *  
     * @author Swinglife 
     *  
     */  
    @Controller  
    public class LoginController {  
      
        /*** 
         * 跳转到登录页面 
         *  
         * @return 
         */  
        @RequestMapping(value = "toLogin")  
        public String toLogin() {  
            // 跳转到/page/login.jsp页面  
            return "login";  
        }  
      
        /*** 
         * 实现用户登录 
         *  
         * @param username 
         * @param password 
         * @return 
         */  
        @RequestMapping(value = "login")  
        public ModelAndView Login(String username, String password) {  
            ModelAndView mav = new ModelAndView();  
            User user = accountService.getUserByUserName(username);  
            if (user == null) {  
                mav.setView("toLogin");  
                mav.addObject("msg", "用户不存在");  
                return mav;  
            }  
            if (!user.getPassword().equals(password)) {  
                mav.setView("toLogin");  
                mav.addObject("msg", "账号密码错误");  
                return mav;  
            }  
            SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());  
            // 登录后存放进shiro token  
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());  
            Subject subject = SecurityUtils.getSubject();  
            subject.login(token);  
            // 登录成功后会跳转到successUrl配置的链接,不用管下面返回的链接。  
            mav.setView("redirect:/home");  
            return mav;  
        }  
      
        // 处理用户业务类  
        @Autowired  
        private AccountService accountService;  
    }  

    6、编写信息认证成功后的跳转页面:

    package org.swinglife.controller;  
      
    import org.springframework.stereotype.Controller;  
    import org.springframework.web.bind.annotation.RequestMapping;  
      
    @Controller  
    public class IndexController {  
      
        @RequestMapping("home")  
        public String index() {  
            System.out.println("登录成功");  
            return "home";  
        }  
    } 

    7、Shiro的配置文件.xml

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
            <property name="securityManager" ref="securityManager" />  
            <property name="loginUrl" value="/toLogin" />  
            <property name="successUrl" value="/home" />  
            <property name="unauthorizedUrl" value="/403" />  
               
            <property name="filterChainDefinitions">  
                <value>  
                    /toLogin = authc <!-- authc 表示需要认证才能访问的页面 -->  
                    /home = authc, perms[/home]  <!-- perms 表示需要该权限才能访问的页面 -->  
                </value>  
            </property>  
        </bean>  
      
      
      
      
        <bean id="myShiroRealm" class="org.swinglife.shiro.MyShiroRealm">  
            <property name="accountService" ref="accountService" />  
        </bean>  
      
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <property name="realm" ref="myShiroRealm"></property>  
        </bean>  
      
        <bean id="accountService" class="org.swinglife.service.AccountService"></bean>  
      
        <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">   
            <property name="cacheManager" ref="cacheManager" /> </bean> -->  

    loginUrl 用于配置登陆页

    successUrl 用于配置登录成功后返回的页面,不过该参数只会在当登录页面中并没有任何返回页面时才会生效,否则会跳转到登录Controller中的指定页面。

    unauthorizedUrl 用于配置没有权限访问页面时跳转的页面

     

    filterChainDefinitions:apache shiro通过filterChainDefinitions参数来分配链接的过滤,资源过滤有常用的以下几个参数:

    1、authc 表示需要认证的链接

    2、perms[/url] 表示该链接需要拥有对应的资源/权限才能访问

    3、roles[admin] 表示需要对应的角色才能访问

    4、perms[admin:url] 表示需要对应角色的资源才能访问

     

    8、登陆页login.jsp

    <body>  
      
    <h1>user login</h1>  
    <form action="login" method="post">  
    username:<input type="text" name="username"><p>  
    password:<input type="password" name="password">  
    <p>  
    ${msg }  
    <input type="submit" value="submit">  
    </form>  
    </body> 

    9、运行程序

    在数据库中添加一条用户、角色、以及权限数据,并且在关联表中添加一条关联数据:

     

     

    在浏览器中访问: home页面 就会跳转到登录页面:  

     

     

    最后输入 账号密码 就会跳转到登录成功页面。

    关于如何实现同一时刻只有一个相同账户登录的问题:

      DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
                DefaultWebSessionManager sessionManager = (DefaultWebSessionManager)securityManager.getSessionManager();
                Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();

    通过以上代码得到所有的session,循环遍历session:

        for(Session session:sessions){
    
            System.out.println("登录ip:"+session.getHost());
    
            System.out.println("登录用户"+session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
    
            System.out.println("最后操作日期:"+session.getLastAccessTime());
    
            }

    然后自行判断是否存在某一个账户,如果不存在可以登录,如果存在return null

    如果只进入了doGetAuthenticationInfo方法,没有进入doGetAuthorizationInfo方法,并且配置无异常,无遗漏,可能是因为在过滤器中权限配置没有配置到你即将进入的页面,如果没有配置perms[],则shiro不会自行进入doGetAuthorizationInfo方法进行权限判断。

     

    shiro jar:http://download.csdn.net/detail/swingpyzf/8766673

    项目源码:github:https://github.com/swinglife/shiro_ex

    本文转自:http://blog.csdn.net/swingpyzf/article/details/46342023/

  • 相关阅读:
    引用dll文件要提交解决方案
    设置dateBox初始日期为当前日期
    Java基础之线程并发协作(生产消费者模式)
    Java基础之线程的交互
    Java基础之线程synchronized关键字
    Java基础之线程阻塞
    Java基础之线程的两种实现方式
    Java Eclipse代码注释和快捷键
    Java基础之数组
    列出一些你常见的运行时异常?
  • 原文地址:https://www.cnblogs.com/fengwenzhee/p/6992995.html
Copyright © 2011-2022 走看看