美文网首页
shiro权限管理

shiro权限管理

作者: lisa_777 | 来源:发表于2018-05-02 17:12 被阅读0次
    这篇文章主要是通过shiro来实现简单的权限管理,以spring boot为基础框架,maven管理.mysql,文章参考: https://blog.csdn.net/ityouknow/article/details/73836159
    
    以下是大致的流程:
    1.创建spring-boot项目,引入相关的依赖;
    2.建模(用户,角色,权限以及之间的关联关系);
    3.准备页面静态文件(index.html,login.html等等)
    4.配置数据库连接信息,初始化部分数据;
    5.创建相关的dao,service用于用户访问登陆的查询;
    6.shiro的相关Config配置;
    7.测试demo.
    

    1.创建spring-boot项目,此案例的版本是1.5.12,引入的模块如下截图:


    image.png

    DevTools-非必须,只是用作热部署.
    项目创建完成后的结构:


    image.png
    引入shiro和HTML的依赖:
           <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>
            <dependency>
                <groupId>net.sourceforge.nekohtml</groupId>
                <artifactId>nekohtml</artifactId>
            </dependency>
    

    2.建模(用户,角色,权限以及之间的关联关系)
    2.1用户UserInfo

    @Entity
    public class UserInfo implements Serializable {
        @Id
        @GeneratedValue
        private Integer uid;
        @Column(unique =true)
        private String username;//帐号
        private String name;//名称(昵称或者真实姓名,不同系统不同定义)
        private String password; //密码;
        private String salt;//加密密码的盐
        private byte state;//用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定.
        @ManyToMany(fetch= FetchType.EAGER)//立即从数据库中进行加载数据;
        @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") })
        private List<SysRole> roleList;// 一个用户具有多个角色
        /**
         * 密码盐.
         * @return
         */
        //重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解
        public String getCredentialsSalt(){
            return this.username+this.salt;
        }
    }
    //省略get,set方法
    

    2.2角色(SysRole)

    @Entity
    public class SysRole {
        @Id@GeneratedValue
        private Integer id; // 编号
        private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的:
        private String description; // 角色描述,UI界面显示使用
        private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户
    
        //角色 -- 权限关系:多对多关系;
        @ManyToMany(fetch= FetchType.EAGER)
        @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")})
        private List<SysPermission> permissions;
    
        // 用户 - 角色关系定义;
        @ManyToMany
        @JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="uid")})
        private List<UserInfo> userInfos;// 一个角色对应多个用户
    }
    //省略get,set方法
    

    2.3权限(SysPermission)

    @Entity
    public class SysPermission implements Serializable {
        @Id@GeneratedValue
        private Integer id;//主键.
        private String name;//名称.
        @Column(columnDefinition="enum('menu','button')")
        private String resourceType;//资源类型,[menu|button]
        private String url;//资源路径.
        private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
        private Long parentId; //父编号
        private String parentIds; //父编号列表
        private Boolean available = Boolean.FALSE;
        @ManyToMany
        @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})
        private List<SysRole> roles;
    }
    //省略get,set方法
    

    3.准备页面静态文件(index.html,login.html等等)
    除了login.html页面比较多内容,其他页面只是显示对应的信息,不会太负责,具体可以参考index.html
    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
    错误信息:<h4 th:text="${msg}"></h4>
    <form action="" method="post">
        <p>账号:<input type="text" name="username" value="admin"/></p>
        <p>密码:<input type="text" name="password" value="123456"/></p>
        <p><input type="submit" value="登录"/></p>
    </form>
    </body>
    </html>
    

    index.html

      <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index</title>
    </head>
    <body>
    <h1>index</h1>
    </body>
    </html>
    

    所有用到的html页面有:


    image.png

    4.配置数据库连接信息,初始化部分数据;
    application.yml

    spring:
        datasource:
          url: jdbc:mysql://localhost:3306/test1
          username: root
          password: admin
          driver-class-name: com.mysql.jdbc.Driver
    
        jpa:
          database: mysql
          show-sql: true
          hibernate:
            ddl-auto: update
            naming:
              strategy: org.hibernate.cfg.DefaultComponentSafeNamingStrategy
          properties:
             hibernate:
                dialect: org.hibernate.dialect.MySQL5Dialect
    
        thymeleaf:
           cache: false
           mode: LEGACYHTML5
    

    thymeleaf的配置是为了去掉html的校验
    此时,启动应用,数据库会生成一下表格


    image.png

    初始化数据:

    INSERT INTO `user_info` (`uid`,`username`,`name`,`password`,`salt`,`state`) VALUES ('1', 'admin', '管理员', '123456', '8d78869f470951332959580424d4bf4f', 0);
    INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (1,0,'用户管理',0,'0/','userInfo:view','menu','userInfo/userList');
    INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (2,0,'用户添加',1,'0/1','userInfo:add','button','userInfo/userAdd');
    INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (3,0,'用户删除',1,'0/1','userInfo:del','button','userInfo/userDel');
    INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (1,0,'管理员','admin');
    INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (2,0,'VIP会员','vip');
    INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (3,1,'test','test');
    INSERT INTO `sys_role_permission` VALUES ('1', '1');
    INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (1,1);
    INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (2,1);
    INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (3,2);
    INSERT INTO `sys_user_role` (`role_id`,`uid`) VALUES (1,1);
    

    5.创建相关的dao,service用于用户访问登陆的查询;
    5.1创建用户的service接口

    public interface UserInfoService {
        UserInfo findByUsername(String username);
    }
    

    5.2创建用户的dao接口

    public interface UserInfoDao extends JpaRepository<UserInfo, Long>{
        public UserInfo findByUsername(String username);
    }
    

    5.3创建service的实现类

    @Service
    public class UserInfoServiceImpl implements UserInfoService{
        
        @Autowired
        private UserInfoDao userInfoDao;
    
        @Override
        public UserInfo findByUsername(String username) {
            return userInfoDao.findByUsername(username);
        }
    }
    

    6.shiro的相关Config配置;
    6.1MyShiroRealm

    public class MyShiroRealm extends AuthorizingRealm{
        @Autowired
        private UserInfoService userInfoService;
        
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
             System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
             SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
             UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
             //获取用户的角色以及角色的权限
             for(SysRole role:userInfo.getRoleList()){
                    authorizationInfo.addRole(role.getRole());
                    for(SysPermission p:role.getPermissions()){
                        authorizationInfo.addStringPermission(p.getPermission());
                    }
                }
               return authorizationInfo;
        }
        /*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
              System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
                //获取用户的输入的账号.
                String username = (String)token.getPrincipal();
                System.out.println(token.getCredentials());
                //通过username从数据库中查找 User对象,如果找到,没找到.
                //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
                UserInfo userInfo = userInfoService.findByUsername(username);
                System.out.println("----->>userInfo="+userInfo);
                if(userInfo == null){
                    return null;
                }
                SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                        userInfo, //用户名
                        userInfo.getPassword(), //密码
                        ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
                        getName()  //realm name
                );
                return authenticationInfo;
        }
    
    }
    

    6.2ShiroConfig

    @Configuration
    public class ShiroConfig {
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            System.out.println("ShiroConfiguration.shirFilter()");
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            //拦截器.
            Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
            // 配置不会被拦截的链接 顺序判断
            filterChainDefinitionMap.put("/static/**", "anon");
            //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/logout", "logout");
            //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
            //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
            filterChainDefinitionMap.put("/**", "authc");
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setLoginUrl("/login");
            // 登录成功后要跳转的链接
            shiroFilterFactoryBean.setSuccessUrl("/index");
    
            //未授权界面;
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public MyShiroRealm myShiroRealm(){
            MyShiroRealm myShiroRealm = new MyShiroRealm();
            return myShiroRealm;
        }
    
    
        @Bean
        public SecurityManager securityManager(){
            DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
            securityManager.setRealm(myShiroRealm());
            return securityManager;
        }
        /**
         *  开启shiro aop注解支持.
         *  使用代理方式;所以需要开启代码支持;
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
        @Bean(name="simpleMappingExceptionResolver")
        public SimpleMappingExceptionResolver
        createSimpleMappingExceptionResolver() {
            SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
            Properties mappings = new Properties();
            mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
            mappings.setProperty("UnauthorizedException","403");
            r.setExceptionMappings(mappings);  // None by default
            r.setDefaultErrorView("error");    // No default
            r.setExceptionAttribute("ex");     // Default is "exception"
            //r.setWarnLogCategory("example.MvcLogger");     // No default
            return r;
        }
    }
    

    7.测试demo.
    7.1HomeController

    @Controller
    public class HomeController {
        @RequestMapping({"/","/index"})
        public String index(){
            return"/index";
        }
    
        @RequestMapping("/login")
        public String login(HttpServletRequest request, Map<String, Object> map) throws Exception{
            System.out.println("HomeController.login()");
            // 登录失败从request中获取shiro处理的异常信息。
            // shiroLoginFailure:就是shiro异常类的全类名.
            String exception = (String) request.getAttribute("shiroLoginFailure");
            System.out.println("exception=" + exception);
            String msg = "";
            if (exception != null) {
                if (UnknownAccountException.class.getName().equals(exception)) {
                    System.out.println("UnknownAccountException -- > 账号不存在:");
                    msg = "UnknownAccountException -- > 账号不存在:";
                } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
                    System.out.println("IncorrectCredentialsException -- > 密码不正确:");
                    msg = "IncorrectCredentialsException -- > 密码不正确:";
                } else if ("kaptchaValidateFailed".equals(exception)) {
                    System.out.println("kaptchaValidateFailed -- > 验证码错误");
                    msg = "kaptchaValidateFailed -- > 验证码错误";
                } else {
                    msg = "else >> "+exception;
                    System.out.println("else -- >" + exception);
                }
            }
            map.put("msg", msg);
            // 此方法不处理登录成功,由shiro进行处理
            return "/login";
        }
    
        @RequestMapping("/403")
        public String unauthorizedRole(){
            System.out.println("------没有权限-------");
            return "403";
        }
    
    }
    

    7.2UserInfoController

    @Controller
    @RequestMapping("/userInfo")
    public class UserInfoController {
    
        /**
         * 用户查询.
         * @return
         */
        @RequestMapping("/userList")
        @RequiresPermissions("userInfo:view")//权限管理;
        public String userInfo(){
            return "userInfo";
        }
    
        /**
         * 用户添加;
         * @return
         */
        @RequestMapping("/userAdd")
        @RequiresPermissions("userInfo:add")//权限管理;
        public String userInfoAdd(){
            return "userInfoAdd";
        }
    
        /**
         * 用户删除;
         * @return
         */
        @RequestMapping("/userDel")
        @RequiresPermissions("userInfo:del")//权限管理;
        public String userDel(){
            return "userInfoDel";
        }
    }
    

    7.3 启动应用,访问
    http://localhost:8080
    会跳转到
    http://localhost:8080/login
    使用admin,密码123456登陆后
    会成功进入index.html页面
    同理的,在没有登陆的情况下,访问
    http://localhost:8080/userInfo/userList
    http://localhost:8080/userInfo/userAdd
    都会跳转到login.html的页面,登陆成功后,能进到对应的用户列表/用户新增页面
    访问http://localhost:8080/userInfo/userDel 会跳到403.html页面,没有权限.
    以上是基于使用这篇文章的初始化数据来测试的,具体可根据需要,给不同的用户不同的角色,给不同的角色不同的权限,那跟上面的结果是会有差异的.

    相关文章

      网友评论

          本文标题:shiro权限管理

          本文链接:https://www.haomeiwen.com/subject/vclcrftx.html