<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何實現(xiàn)登錄、URL 和頁面按鈕的訪問控制

          共 17872字,需瀏覽 36分鐘

           ·

          2021-10-23 08:21

          來源:社會主義接班人

          cnblogs.com/5ishare/p/10461073.html

          • 一、引入依賴

          • 二、增加 Shiro 配置

          • 三、自定義 Realm

          • 四、登錄認(rèn)證

          • 五、Controller 層訪問控制

          • 六、前端頁面層訪問控制

          • 七、小結(jié)


          用戶權(quán)限管理一般是對用戶頁面、按鈕的訪問權(quán)限管理。Shiro 框架是一個強大且易用的 Java 安全框架,執(zhí)行身份驗證、授權(quán)、密碼和會話管理,對于 Shiro 的介紹這里就不多說。本篇博客主要是了解 Shiro 的基礎(chǔ)使用方法,在權(quán)限管理系統(tǒng)中集成 Shiro 實現(xiàn)登錄、url 和頁面按鈕的訪問控制。

          一、引入依賴

          使用 SpringBoot 集成 Shiro 時,在 pom.xml 中可以引入 shiro-spring-boot-web-starter。由于使用的是 thymeleaf 框架,thymeleaf 與 Shiro 結(jié)合需要 引入 thymeleaf-extras-shiro。

          <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring-boot-web-starter -->
                  <dependency>
                      <groupId>org.apache.shiro</groupId>
                      <artifactId>shiro-spring-boot-web-starter</artifactId>
                      <version>1.4.0</version>
                  </dependency>
                  <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
                  <dependency>
                      <groupId>com.github.theborakompanioni</groupId>
                      <artifactId>thymeleaf-extras-shiro</artifactId>
                      <version>2.0.0</version>
                  </dependency>

          二、增加 Shiro 配置

          有哪些 url 是需要攔截的,哪些是不需要攔截的,登錄頁面、登錄成功頁面的 url、自定義的 Realm 等這些信息需要設(shè)置到 Shiro 中,所以創(chuàng)建 Configuration 文件 ShiroConfig。

          package com.example.config;


          import org.apache.shiro.mgt.SecurityManager;
          import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
          import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
          import org.springframework.beans.factory.annotation.Qualifier;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;

          import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

          import java.util.LinkedHashMap;
          import java.util.Map;


          @Configuration
          public class ShiroConfig {
              @Bean("shiroFilterFactoryBean")
              public ShiroFilterFactoryBean shiroFilterFactoryBean(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已經(jīng)替我們實現(xiàn)了
                  filterChainDefinitionMap.put("/logout""logout");
                  //<!-- 過濾鏈定義,從上向下順序執(zhí)行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了;
                  //<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->
                  filterChainDefinitionMap.put("/**""authc");
                  // 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login.jsp"頁面
                  shiroFilterFactoryBean.setLoginUrl("/login");
                  // 登錄成功后要跳轉(zhuǎn)的鏈接
                  shiroFilterFactoryBean.setSuccessUrl("/index");

                  //未授權(quán)界面;
                  shiroFilterFactoryBean.setUnauthorizedUrl("/403");
                  shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
                  return shiroFilterFactoryBean;
              }

              @Bean()    //創(chuàng)建DefaultWebSecurityManager
              public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")MyShiroRealm userRealm){
                  DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
                  defaultWebSecurityManager.setRealm(userRealm);
                  return defaultWebSecurityManager;

              }
              //創(chuàng)建Realm
              @Bean()
              public MyShiroRealm getUserRealm(){
                  return new MyShiroRealm();

              }

              @Bean
              public ShiroDialect shiroDialect() {
                  return new ShiroDialect();
              }
          }

          ShiroDialect 這個 bean 對象是在 thymeleaf 與 Shiro 結(jié)合,前端 html 訪問 Shiro 時使用。

          三、自定義 Realm

          在自定義的 Realm 中繼承了 AuthorizingRealm 抽象類,重寫了兩個方法:doGetAuthorizationInfo 和 doGetAuthenticationInfo。doGetAuthorizationInfo 主要是用來處理權(quán)限配置,doGetAuthenticationInfo 主要處理身份認(rèn)證。

          這里在 doGetAuthorizationInfo 中,將 role 表的 id 和 permission 表的 code 分別設(shè)置到 SimpleAuthorizationInfo 對象中的 role 和 permission 中。

          還有一個地方需要注意:@Component("authorizer"),剛開始我沒設(shè)置,但報錯提示需要一個 authorizer 的 bean,查看 AuthorizingRealm 可以發(fā)現(xiàn)它 implements 了 Authorizer,所以在自定義的 realm 上添加 @Component("authorizer") 就可以了。

          package com.example.config;
          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.authz.AuthorizationInfo;
          import org.apache.shiro.authz.SimpleAuthorizationInfo;
          import org.apache.shiro.realm.AuthorizingRealm;
          import org.apache.shiro.subject.PrincipalCollection;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Component;
          import com.example.pojo.Permission;
          import com.example.pojo.Role;
          import com.example.pojo.User;
          import com.example.service.RoleService;
          import com.example.service.UserService;

          @Component("authorizer")
          public class MyShiroRealm extends AuthorizingRealm {

              @Autowired
              private UserService userService;

              @Autowired
              private RoleService roleService;

              @Override
              protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
                  System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
                  SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                  User user  = (User)principals.getPrimaryPrincipal();
                  System.out.println("User:"+user.toString()+" roles count:"+user.getRoles().size());
                  for(Role role:user.getRoles()){
                      authorizationInfo.addRole(role.getId());
                      role=roleService.getRoleById(role.getId());
                      System.out.println("Role:"+role.toString());
                      for(Permission p:role.getPermissions()){
                          System.out.println("Permission:"+p.toString());
                          authorizationInfo.addStringPermission(p.getCode());
                      }
                  }
                  System.out.println("權(quán)限配置-->authorizationInfo"+authorizationInfo.toString());
                  return authorizationInfo;
              }

              /*主要是用來進行身份認(rèn)證的,也就是說驗證用戶輸入的賬號和密碼是否正確。*/
              @Override
              protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
                      throws AuthenticationException {
                  System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
                  //獲取用戶的輸入的賬號.
                  String username = (String)token.getPrincipal();
                  System.out.println(token.getCredentials());
                  //通過username從數(shù)據(jù)庫中查找 User對象,如果找到,沒找到.
                  //實際項目中,這里可以根據(jù)實際情況做緩存,如果不做,Shiro自己也是有時間間隔機制,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法
                  User user = userService.getUserById(username);
                  System.out.println("----->>userInfo="+user);
                  if(user == null){
                      return null;
                  }
                  SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                          user, //用戶名
                          "123456"//密碼
                          getName()  //realm name
                  );
                  return authenticationInfo;
              }

          }

          四、登錄認(rèn)證

          1. 登錄頁面


          這里做了一個非常丑的登錄頁面,主要是自己懶,不想在網(wǎng)上復(fù)制粘貼找登錄頁面了。

          <!DOCTYPE html>
          <head>
              <meta charset="utf-8">
              <title></title>
              <meta >
              <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
              <meta >
              <meta >
              <meta >
              <meta >
          </head>

          <form action="/login" method="post">
                <label>用戶名:</label><input type="text"  ><br>
                <label >密碼:</label><input type="text"  ><br>
                <button type="submit">登錄</button><button type="reset">取消</button>
          </form>
          </body>
          </html>
          1. 處理登錄請求


          在 LoginController 中通過登錄名、密碼獲取到 token 實現(xiàn)登錄。

          package com.example.controller;
          import org.apache.shiro.SecurityUtils;
          import org.apache.shiro.authc.IncorrectCredentialsException;
          import org.apache.shiro.authc.UnknownAccountException;
          import org.apache.shiro.authc.UsernamePasswordToken;
          import org.apache.shiro.subject.Subject;
          import org.springframework.stereotype.Controller;
          import org.springframework.ui.Model;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RequestMethod;

          @Controller
          public class LoginController {

              //退出的時候是get請求,主要是用于退出
              @RequestMapping(value = "/login",method = RequestMethod.GET)
              public String login(){
                  return "login";
              }

              //post登錄
              @RequestMapping(value = "/login",method = RequestMethod.POST)
              public String login(Model model,String id,String pwd){
                  //添加用戶認(rèn)證信息
                  Subject subject = SecurityUtils.getSubject();
                  UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                          id,
                          "123456");
                  try {
                          subject.login(usernamePasswordToken);
                          return "home";
                      }
                  catch (UnknownAccountException e) {
                      //用戶名不存在
                      model.addAttribute("msg","用戶名不存在");
                      return "login";
                      }catch (IncorrectCredentialsException e) {
                          //密碼錯誤
                          model.addAttribute("msg","密碼錯誤");
                          return "login";
                          }

              }
              @RequestMapping(value = "/index")
              public String index(){
                  return "home";
              }

          }

          五、Controller 層訪問控制

          1. 首先來數(shù)據(jù)庫的數(shù)據(jù),兩張圖是用戶角色、和角色權(quán)限的數(shù)據(jù)。


          1. 設(shè)置權(quán)限


          這里在用戶頁面點擊編輯按鈕時設(shè)置需要有 id=002 的角色,在點擊選擇角色按鈕時需要有 code=002 的權(quán)限。

          @RequestMapping(value = "/edit",method = RequestMethod.GET)
              @RequiresRoles("002")//權(quán)限管理;
              public String editGet(Model model,@RequestParam(value="id") String id) {
                  model.addAttribute("id", id);
                  return "/user/edit";
              }
          @RequestMapping(value = "/selrole",method = RequestMethod.GET)
              @RequiresPermissions("002")//權(quán)限管理;
              public String selctRole(Model model,@RequestParam("id") String id,@RequestParam("type") Integer type) {
                  model.addAttribute("id",id);
                  model.addAttribute("type", type);
                  return "/user/selrole";
              }

          當(dāng)使用用戶 001 登錄時,點擊編輯,彈出框如下,提示沒有 002 的角色

          點擊選擇角色按鈕時提示沒有 002 的權(quán)限。

          當(dāng)使用用戶 002 登錄時,點擊編輯按鈕,顯示正常,點擊選擇角色也是提示沒 002 的權(quán)限,因為權(quán)限只有 001。

          六、前端頁面層訪問控制

          有時為了不想像上面那樣彈出錯誤頁面,需要在按鈕顯示上進行不可見,這樣用戶也不會點擊到。前面已經(jīng)引入了依賴并配置了 bean,這里測試下在 html 中使用 shiro。

          1. 首先設(shè)置 html 標(biāo)簽引入 shiro


          <html xmlns:th="http://www.thymeleaf.org"
                xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">


          1. 控制按鈕可見


          這里使用 shiro:hasAnyRoles="002,003" 判斷用戶角色是否是 002 或 003,是則顯示不是則不顯示。

              <div class="layui-inline">
                  <a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-normal newsAdd_btn" onclick="addUser('')">添加用戶</a>
              </div>
              <div class="layui-inline">
                  <a shiro:hasAnyRoles="002,003" class="layui-btn layui-btn-danger batchDel" onclick="getDatas();">批量刪除</a>
              </div>


          當(dāng) 001 用戶登錄時,添加用戶、批量刪除按鈕都不顯示,只顯示查詢按鈕。

          當(dāng) 002 用戶登錄時,添加用戶、批量刪除按鈕都顯示

          七、小結(jié)

          這里只是實現(xiàn)了 Shiro 的簡單的功能,Shiro 還有很多很強大的功能,比如 session 管理等,而且目前權(quán)限管理模塊還有很多需要優(yōu)化的功能,左側(cè)導(dǎo)航欄的動態(tài)加載和權(quán)限控制、Shiro 與 Redis 結(jié)合實現(xiàn) session 共享、Shiro 與 Cas 結(jié)合實現(xiàn)單點登錄等。后續(xù)可以把項目做為開源項目,慢慢完善集成更多模塊例如:Swagger2、Redis、Druid、RabbitMQ 等供初學(xué)者參考。


          怎么接私活?這個渠道你100%有用!請收藏!


          在看 
          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  九哥超逼网 | 日本黄色片一级视频 | av黄色电影一区天堂一区二区三区 | 热久久蜜芽| 大香蕉婷婷在线 |