• 7. 整合shiro,搭建粗粒度权限管理


    shiro是一个易用的权限管理框架,只需提供一个Realm即可在项目中使用,本文就将结合上一篇中搭建的权限模块、角色模块和用户模块来搭建一个粗粒度的权限管理系统,具体如下:
    1. 添加shiro依赖和与thymeleaf集成的依赖,并更新项目:

     1 <dependency>
     2     <groupId>org.apache.shiro</groupId>
     3     <artifactId>shiro-spring</artifactId>
     4     <version>1.4.0</version>
     5 </dependency>
     6 <dependency>  
     7     <groupId>com.github.theborakompanioni</groupId>  
     8     <artifactId>thymeleaf-extras-shiro</artifactId>  
     9     <version>1.0.2</version>  
    10 </dependency> 
    pom.xml

    2. 添加自定义Realm类,主要完成用户校验和授权的功能

     1 package com.lvniao.blog.config;
     2 
     3 import java.util.HashSet;
     4 import java.util.Set;
     5 
     6 import javax.annotation.Resource;
     7 
     8 import org.apache.shiro.authc.AuthenticationException;
     9 import org.apache.shiro.authc.AuthenticationInfo;
    10 import org.apache.shiro.authc.AuthenticationToken;
    11 import org.apache.shiro.authc.SimpleAuthenticationInfo;
    12 import org.apache.shiro.authc.UsernamePasswordToken;
    13 import org.apache.shiro.authz.AuthorizationInfo;
    14 import org.apache.shiro.authz.SimpleAuthorizationInfo;
    15 import org.apache.shiro.realm.AuthorizingRealm;
    16 import org.apache.shiro.subject.PrincipalCollection;
    17 import org.apache.shiro.util.ByteSource;
    18 import org.springframework.beans.factory.annotation.Autowired;
    19 import org.springframework.stereotype.Component;
    20 
    21 import com.lvniao.blog.mapper.UserMapper;
    22 import com.lvniao.blog.model.User;
    23 
    24 @Component
    25 public class ShiroRealm extends AuthorizingRealm {
    26 
    27     @Autowired
    28     private UserMapper userMapper;
    29     
    30     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    31         String username = (String)principals.getPrimaryPrincipal();
    32         User user = userMapper.getUserWithRoleByName(username);
    33         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    34         Set<String> roles = new HashSet<String>();
    35         Set<String> permissions = new HashSet<String>();
    36         user.getRolesAndPermissions(roles, permissions);
    37         authorizationInfo.setRoles(roles);
    38         authorizationInfo.setStringPermissions(permissions);
    39         return authorizationInfo;
    40     }
    41     
    42     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    43         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
    44         String username = (String)usernamePasswordToken.getPrincipal();
    45         String password = new String(usernamePasswordToken.getPassword());
    46         if(userMapper.check(username, password) != null) {
    47             User user = userMapper.getUserByName(username);
    48             
    49             SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getName(), 
    50                     user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
    51             
    52             return authenticationInfo;
    53         } else {
    54             throw new AuthenticationException();
    55         }
    56     }
    57 }
    ShiroRealm

    3. 添加ShiroConfiguration类,完成shiro的配置

     1 package com.lvniao.blog.config;
     2 
     3 import java.util.LinkedHashMap;
     4 import java.util.List;
     5 import java.util.Map;
     6 
     7 import org.apache.shiro.mgt.SessionsSecurityManager;
     8 import org.apache.shiro.realm.Realm;
     9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    10 import org.apache.shiro.web.mgt.CookieRememberMeManager;
    11 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    12 import org.springframework.beans.factory.annotation.Qualifier;
    13 import org.springframework.context.annotation.Bean;
    14 import org.springframework.context.annotation.Configuration;
    15 import org.apache.shiro.mgt.SecurityManager;
    16 
    17 @Configuration
    18 public class ShiroConfiguration{
    19 
    20     @Bean
    21     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    22         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    23 
    24         // 必须设置 SecurityManager
    25         shiroFilterFactoryBean.setSecurityManager(securityManager);
    26 
    27         shiroFilterFactoryBean.setLoginUrl("/admin/login");
    28 
    29         shiroFilterFactoryBean.setSuccessUrl("/admin/");
    30 
    31         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    32         filterChainDefinitionMap.put("/css/**", "anon");
    33         filterChainDefinitionMap.put("/js/**", "anon");
    34         filterChainDefinitionMap.put("/images/**", "anon");
    35         filterChainDefinitionMap.put("/fonts/**", "anon");
    36         filterChainDefinitionMap.put("/admin/loginaction", "anon");
    37 
    38         filterChainDefinitionMap.put("/**", "authc");
    39 
    40         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    41         System.out.println("Shiro拦截器工厂类注入成功");
    42         return shiroFilterFactoryBean;
    43     }
    44 
    45     @Bean
    46     public SecurityManager securityManager() {
    47         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    48         // 设置realm.
    49         securityManager.setRealm(ShiroRealm());
    50         return securityManager;
    51     }
    52 
    53     @Bean
    54     public ShiroRealm ShiroRealm() {
    55         ShiroRealm shiroRealm = new ShiroRealm();
    56         return shiroRealm;
    57     }
    58 
    59     @Bean
    60     public ShiroDialect shiroDialect() {
    61         return new ShiroDialect();
    62     }
    63 }
    ShiroConfiguration

    因为添加了filterChainDefinitionMap.put("/**", "authc"),所以shiro会对所有请求进行验证,包括静态资源,所以需要通过如: filterChainDefinitionMap.put("/css/**", "anon");这样的来使静态资源请求不被拦截。通过这三个步骤就已经把shiro配置好了,下面就完成登录部分:
    4. 添加登录action

     1 package com.lvniao.blog.admin.controller;
     2 
     3 import java.util.List;
     4 
     5 import org.apache.shiro.SecurityUtils;
     6 import org.apache.shiro.authc.IncorrectCredentialsException;
     7 import org.apache.shiro.authc.UnknownAccountException;
     8 import org.apache.shiro.authc.UsernamePasswordToken;
     9 import org.apache.shiro.crypto.hash.Md5Hash;
    10 import org.apache.shiro.subject.Subject;
    11 import org.springframework.beans.factory.annotation.Autowired;
    12 import org.springframework.stereotype.Controller;
    13 import org.springframework.ui.Model;
    14 import org.springframework.web.bind.annotation.RequestMapping;
    15 import org.springframework.web.servlet.ModelAndView;
    16 
    17 import com.lvniao.blog.mapper.MenuMapper;
    18 import com.lvniao.blog.mapper.UserMapper;
    19 import com.lvniao.blog.model.Menu;
    20 import com.lvniao.blog.model.User;
    21 import com.lvniao.blog.util.Constant;
    22 
    23 @Controller
    24 @RequestMapping("/admin")
    25 public class AdminController {
    26     
    27     @Autowired
    28     private UserMapper userMapper;
    29     
    30     @Autowired
    31     private MenuMapper menuMapper;
    32     
    33     @RequestMapping("/")
    34     public String index(Model model) {
    35         model.addAttribute("menus", menuMapper.getParentMenu());
    36         return "admin/index";
    37     }
    38     
    39     @RequestMapping("/menu")
    40     public String menu(Model model) {
    41         return "admin/menu1";
    42     }
    43     
    44     @RequestMapping("/login")
    45     public String login() {
    46         return "admin/login";
    47     }
    48     
    49     @RequestMapping("/loginaction")
    50     public ModelAndView loginAction(User user) {
    51         Subject subject = SecurityUtils.getSubject();
    52         if(!subject.isAuthenticated()) {
    53             
    54             try {
    55                 User u = userMapper.getUserByName(user.getName());
    56                 if(u != null) {
    57                     String password = new Md5Hash(user.getPassword(), u.getSalt()).toString();
    58                     UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), password);
    59                     token.setRememberMe(false);
    60                     subject.login(token);
    61                     User currentUser = userMapper.getUserByName(user.getName());
    62                     subject.getSession().setAttribute(Constant.CurrentUser, currentUser);
    63                     return new ModelAndView("redirect:/admin/");
    64                 }
    65                 
    66             } catch(UnknownAccountException ex) {
    67                 
    68             } catch (IncorrectCredentialsException ex) {
    69                 
    70             } catch (Exception ex) {
    71                 
    72             }
    73         }
    74         return new ModelAndView("redirect:/admin/login");
    75     }
    76 }
    AdminController

    5. 添加登录页

     1 <!DOCTYPE HTML>
     2 <html>
     3 <head>
     4     <title>lvniao</title>
     5     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
     6     <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
     7     <link rel="stylesheet" th:href="@{/css/base.css}"/>
     8     <link rel="stylesheet" th:href="@{/css/font-awesome.css}"/>
     9     <link rel="stylesheet" th:href="@{/css/login.css}"/>
    10     <script th:src="@{/js/jquery-3.2.1.js}"></script>
    11     <script th:src="@{/statics/js/base.js}"></script>
    12     <style>
    13         *{
    14             margin:0px;
    15             padding:0px;
    16         }
    17         body{
    18             background-image:url("/images/loginbg.jpg") ;
    19             background-size:100% 100%;
    20         }
    21     </style>
    22 </head>
    23 <body>
    24     <form class="login" th:action="@{/admin/loginaction}" th:method="post">
    25         <div class="header">
    26             <h1>登录</h1>
    27         </div>
    28         <div class="text name">
    29             <input type="text" class="input" name="name" placeholder="用户名"/>
    30         </div>
    31         <div class="text password">
    32             <input type="password" class="input" name="password" placeholder="密码"/>
    33         </div>
    34         <button type="submit" class="button">登录</button>
    35         <div class="foot">
    36         </div>
    37     </form>
    38 </body>
    39 </html>
    login.html

    其中在css中使用url可以按照正常的使用,不用按照html中添加资源的方式。

    登录成功后就会跳转到/admin/页

    6. 因为在上一篇文章中以及添加好了角色授权和给用户赋角色,而现在要做的就是使用shiro来对界面做粗粒度权限管理,shiro在验证授权时需要获得用户的角色列表和权限列表,所以首先就修改User.java,增加获取角色和权限的接口,代码如下:

      1 package com.lvniao.blog.model;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 import java.util.Set;
      6 
      7 import com.lvniao.blog.model.Permission;
      8 import com.lvniao.blog.model.Role;
      9 import com.lvniao.blog.util.StringUtil;
     10 
     11 public class User {
     12 
     13 private Long id;
     14     
     15     private String name;
     16     
     17     private String password;
     18     
     19     private String salt;
     20     
     21     private Boolean locked;
     22 
     23     private List<Role> roles = new ArrayList<Role>();
     24     
     25     private String rolesStr = null;
     26     
     27     public Long getId() {
     28         return id;
     29     }
     30 
     31     public void setId(Long id) {
     32         this.id = id;
     33     }
     34 
     35     public String getName() {
     36         return name;
     37     }
     38 
     39     public void setName(String name) {
     40         this.name = name;
     41     }
     42 
     43     public String getPassword() {
     44         return password;
     45     }
     46 
     47     public void setPassword(String password) {
     48         this.password = password;
     49     }
     50 
     51     public String getSalt() {
     52         return salt;
     53     }
     54 
     55     public void setSalt(String salt) {
     56         this.salt = salt;
     57     }
     58 
     59     public Boolean isLocked() {
     60         return locked;
     61     }
     62 
     63     public void setLocked(Boolean locked) {
     64         this.locked = locked;
     65     }
     66 
     67     public List<Role> getRoles() {
     68         return roles;
     69     }
     70 
     71     public void setRoles(List<Role> roles) {
     72         this.roles = roles;
     73     }
     74 
     75     public String getRolesStr() {
     76         if(StringUtil.isNullOrEmpty(rolesStr)) {
     77             if(getRoles() != null && getRoles().size() > 0) {
     78                 StringBuffer sb = new StringBuffer();
     79                 
     80                 for(Role r : getRoles()) {
     81                     if(r != null) {
     82                         sb.append(r.getId());
     83                         sb.append(",");
     84                     }
     85                 }
     86                 if(sb.length() > 0) {
     87                     rolesStr = sb.toString().substring(0, sb.length() - 1);
     88                 }
     89             }
     90         }
     91         return rolesStr;
     92     }
     93 
     94     public void setRolesStr(String rolesStr) {
     95         this.rolesStr = rolesStr;
     96     }
     97 
     98     public void getRolesAndPermissions(Set<String> roleres, Set<String> permissionres) {
     99         getRolesAndPermissions(getRoles(), roleres, permissionres);
    100     }
    101     
    102     private void getRolesAndPermissions(List<Role> roles, Set<String> roleres, Set<String> permissionres) {
    103         for(Role role : roles) {
    104             roleres.add(role.getAlias());
    105             for(Permission p : role.getPermissions()) {
    106                 permissionres.add(p.getAlias());
    107             }
    108             getRolesAndPermissions(role.getChildren(), roleres, permissionres);
    109         }
    110     }
    111 }
    User.java

    使用时通过getRolesAndPermissions方法获得用户的角色集合和权限集合,已经在ShiroRealm中添加好了。然后就是在页面上使用权限管理;
    7. 这儿以不同角色用户登录后,在用户管理界面上展示对操作权限的限制为例,用户管理界面的代码如下:

      1 <!DOCTYPE html>
      2 <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
      3       xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
      4 <head>
      5     <meta charset="UTF-8"/>
      6     <title></title>
      7     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      8     <link rel="stylesheet" th:href="@{/css/page.css}"/>
      9     <link rel="stylesheet" type="text/css" th:href="@{/easyui/themes/default/easyui.css}"/>
     10     <link rel="stylesheet" type="text/css" th:href="@{/easyui/themes/icon.css}"/>
     11     <link rel="stylesheet" type="text/css" th:href="@{/layui/css/layui.css}"/>
     12     <link rel="stylesheet" type="text/css" th:href="@{/css/grid.css}"/>
     13     <script type="text/javascript" th:src="@{/easyui/jquery.min.js}"></script>
     14     <script type="text/javascript" th:src="@{/easyui/jquery.easyui.min.js}"></script>
     15     <script type="text/javascript" th:src="@{/layui/layui.js}"></script>
     16     <script type="text/javascript" th:src="@{/layui/layui.all.js}"></script>
     17     <script type="text/javascript" th:src="@{/js/form.js}"></script>
     18     <script>
     19         $(function(){
     20             $("#grid").treegrid({url: '/user/list',
     21                 method: 'get',
     22                 rownumbers: true,
     23                 idField: 'id',
     24                 fit:true,
     25                 treeField: 'name',
     26                 pagination: true,
     27                 pageSize: 10,
     28                 pageList: [10, 50, 100]});
     29             $("#btnSearch").click(function(){
     30                 $("#grid").treegrid({queryParams:form2Json("query")});
     31             });
     32             
     33             $(".lv-btn-group .lv-btn").click(function(){
     34                 var type = $(this).attr("type");
     35                 var addr = $(this).attr("addr");
     36                 if(type == "refresh"){
     37                     $(".lv-txtSearch").val("");
     38                     $("#grid").treegrid({queryParams:form2Json("query")});
     39                     return;
     40                 }
     41                 if(type == "add"){
     42                     layer.open({
     43                           type: 2,
     44                           title: $(this).attr("title"),
     45                           shadeClose: true,
     46                           shade: 0.8,
     47                           area: ['330px', '270px'],
     48                           content: addr,
     49                           success:function(layero){
     50                               $("#grid").treegrid({queryParams:form2Json("query")});
     51                           }
     52                         });
     53                     return;
     54                 }
     55                 
     56                 
     57                 var row = $("#grid").treegrid("getSelected");
     58                 if(row == null){
     59                     layer.alert("请选择待操作的数据");
     60                     return;
     61                 }
     62                 if(type == "assign"){
     63                     layer.open({
     64                           type: 2,
     65                           title: $(this).attr("title"),
     66                           shadeClose: true,
     67                           shade: 0.8,
     68                           area: ['330px', '300px'],
     69                           content: addr + "/" + row.id,
     70                           success:function(layero){
     71                               $("#grid").treegrid({queryParams:form2Json("query")});
     72                           }
     73                         });
     74                     return;
     75                 }
     76                 if(type == "modify"){
     77                     layer.open({
     78                       type: 2,
     79                       title: $(this).attr("title"),
     80                       shadeClose: true,
     81                       shade: 0.8,
     82                       area: ['330px', '270px'],
     83                       content: addr + "/" + row.id,
     84                       success:function(layero){
     85                           $("#grid").treegrid({queryParams:form2Json("query")});
     86                       }
     87                     });
     88                     return;
     89                 }
     90                 if(type == "delete"){
     91                     $.ajax({
     92                         url:addr + "/" + row.id,
     93                         type:"get",
     94                         success:function(data){
     95                             $("#grid").treegrid({queryParams:form2Json("query")});
     96                         }
     97                     });
     98                     return;
     99                 }
    100                 
    101             });
    102         });
    103     </script>
    104 </head>
    105 <body>
    106     <div class="lv-option-container">
    107         <form id="query" shiro:hasPermission="user:query" >
    108             <input type="text" class="lv-txtSearch" name="query"/><div class="lv-btnSearch" id="btnSearch">查询</div>
    109         </form>
    110         
    111         <div class="lv-btn-group">
    112             <div class="lv-btn" type="refresh" shiro:hasPermission="user:refresh">刷新</div>
    113             <div class="lv-btn" type="add" addr="/user/add" shiro:hasPermission="user:add" title="添加">添加</div>
    114             <div class="lv-btn" type="modify" addr="/user/modify" shiro:hasPermission="user:modify" title="修改">修改</div>
    115             <div class="lv-btn" type="assign" addr="/user/assign" shiro:hasPermission="user:assign" style="100px;" title="删除">分配角色</div>
    116             <div class="lv-btn" type="delete" addr="/user/delete" shiro:hasPermission="user:delete"  title="删除">删除</div>
    117         </div>
    118     </div>
    119     <div class="lv-grid-container">
    120         <table id="grid">
    121             <thead>
    122                 <tr>
    123                     <th data-options="field:'name'" width="220">用户名</th>
    124                     <th data-options="field:'locked', formatter:booleanFormat" width="220">是否锁定</th>
    125                 </tr>
    126             </thead>
    127         </table>
    128     </div>
    129 </body>
    130 </html>
    index.html

    首先要在HTML标记中添加<html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
    xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">,然后在需要控制权限的界面元素上通过shiro:hasPermission="xxx"来验证当前用户是否有权限操作该元素,如上就完成了粗粒度的权限管理,下面是分别有所有权限和只有查询和刷新权限的界面:

    代码下载路径:https://pan.baidu.com/s/1VqcDk9BIM10-HVyfmrjMHg, 密码:vxtg

  • 相关阅读:
    DB2数据常用指令
    HTMLParser使用
    面试知识点总结之数据库
    面试知识点总结之算法
    面试知识点总结之操作系统
    面试知识点总结之计算机网络
    面试知识点总结之Java语言
    一个精确匹配的问题
    【转】矩阵求导,矩阵计算
    【转】用C语言扩展Python的功能
  • 原文地址:https://www.cnblogs.com/lvniao/p/9040454.html
Copyright © 2020-2023  润新知