天天看點

SpringBoot整合Shiro權限管理SpringBoot整合Shiro權限管理shiro标簽的使用

SpringBoot整合Shiro權限管理

Shiro(Java安全架構)

什麼是權限管理

權限管理包括使用者身份認證和授權兩部分,簡稱認證授權。對于需要通路控制的資源使用者首先經過身份認證,認證通過後使用者具有該資源的通路權限方可通路。

1.什麼是Shiro

Shiro是一個強大且易用的Java安全架構,執行身份驗證、授權、密碼和會話管理。

1.三個核心元件:Subject, SecurityManager 和 Realms.

Subject:即“目前操作使用者”。但是,在Shiro中,Subject這一概念并不僅僅指人,也可以是第三方程序、背景帳戶(Daemon Account)或其他類似事物。它僅僅意味着“目前跟軟體互動的東西”。

Subject代表了目前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。

SecurityManager:它是Shiro架構的核心,典型的Facade模式,Shiro通過SecurityManager來管理内部元件執行個體,并通過它來提供安全管理的各種服務。

Realm: Realm充當了Shiro與應用安全資料間的“橋梁”或者“連接配接器”。也就是說,當對使用者執行認證(登入)和授權(通路控制)驗證時,Shiro會從應用配置的Realm中查找使用者及其權限資訊。

2.Shiro四大核心功能:Authentication,Authorization,Cryptography,Session Management

SpringBoot整合Shiro權限管理SpringBoot整合Shiro權限管理shiro标簽的使用

3.使用者名密碼認證流程

SpringBoot整合Shiro權限管理SpringBoot整合Shiro權限管理shiro标簽的使用
4.授權流程
SpringBoot整合Shiro權限管理SpringBoot整合Shiro權限管理shiro标簽的使用

先建立一個SpringBoot項目,添加shiro的相關依賴

<!--添加 shiro 依賴-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
           
修改配置檔案application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/project?serverTimezone=UTC
    username: root
    password: 123456
  thymeleaf:
    cache: false
    encoding: utf-8
    prefix: classpath:/templates/
mybatis:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.shiro.login.domain
           

實體類

使用者

@Data
public class User {

  private String id;
  private String name;
  private String password;

}

           

角色

@Data
public class Role {

  private String id;
  private String name;

}

           

權限

@Data
public class Permission {

  private String id;
  private String name;
  private String url;
}
           

Mapper.xml和Service層就不貼了

ShiroConfiguration

package com.shiro.login.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

/**
 * @Author: Adorez
 * @Date: 2019/11/27 18:00
 * @Description:
 */
@Configuration
public class ShiroConfiguration {

    @Bean("userRealm")
    public UserRealm userRealm(){
        UserRealm userRealm=new UserRealm();
        return userRealm;
    }

    /**
     * 定義安全管理器 securityManager
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(userRealm);
        return manager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        //設定securityManager
        bean.setSecurityManager(securityManager);
        //設定登入頁面
        bean.setLoginUrl("/login");
        //設定登入成功跳轉的頁面
        bean.setSuccessUrl("/success");
        bean.setUnauthorizedUrl("/error/unAuth");
        //定義過濾器
        LinkedHashMap<String,String> filter=new LinkedHashMap<>();
        filter.put("/","authc");//首頁需要認證
        filter.put("/login","anon");//登入頁面無需認證
        filter.put("/toLogin","anon");//登入頁面無需認證
        filter.put("/vip/index","roles[vip]");//添權重限配置
        filter.put("/error/unAuth","authc");
        //需要登入通路的資源,一般将/**放在最下邊
        filter.put("/**","authc");
        bean.setFilterChainDefinitionMap(filter);
        return bean;
    }

    /**
     * 配置shiro和spring的關聯
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * lifecycleBeanPostProcessor 是負責生命周期的 初始化和銷毀的類
     * @return
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    /**
     * Spring 的一個 bean , 由 Advisor 決定對哪些類的方法進行 AOP 代理 .
     * @return
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator
    getDefaultAdvisorAutoProxyCreator(){
        return new DefaultAdvisorAutoProxyCreator();
    }
    /**
     * 頁面上使用shiro标簽
     * @return
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}
           
UserRealm
package com.shiro.login.config;

import com.shiro.login.domain.Permission;
import com.shiro.login.domain.Role;
import com.shiro.login.domain.User;
import com.shiro.login.service.PermissionService;
import com.shiro.login.service.RoleService;
import com.shiro.login.service.UserService;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @Author: Adorez
 * @Date: 2019/11/27 18:13
 * @Description:
 */
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    /**
     * 為使用者授權
     * 角色權限和對應的權限添加
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("為使用者授權");
        //擷取登入使用者名
        User userInfo = (User) principalCollection.getPrimaryPrincipal();
        //查詢使用者名稱
        User user = userService.findUserByuserName(userInfo.getName());
        //添加角色和權限
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        //根據查詢出來的使用者資訊獲得角色
        Role role = roleService.findById(Integer.valueOf(user.getId()));
        //添加角色1
        simpleAuthorizationInfo.addRole(role.getName());
        //根據查詢出來的角色查詢權限
        List<Permission> perList = permissionService.findById(Integer.valueOf(role.getId()));

        for (Permission per:perList) {
            //添權重限
            simpleAuthorizationInfo.addStringPermission(per.getName());
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 認證登入
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("---認證登入");
        //token攜帶了使用者資訊
        UsernamePasswordToken usernamePasswordToken=(UsernamePasswordToken)token;
        //擷取前端輸入的使用者名
        String username = usernamePasswordToken.getUsername();
        //根據使用者名查詢資料庫中對應的記錄
        User user = userService.findUserByuserName(username);
        if (null==user) {
            new UnknownAccountException("賬戶不存在");
        }else {
            new IncorrectCredentialsException("密碼不正确");
        }
        //目前realm對象的name
        String realmName = getName();
        //鹽值
        ByteSource source=ByteSource.Util.bytes(user.getName());
        /**
         * 封裝使用者資訊,建構authenticationInfo對象并傳回
         * 參數一:使用者對象
         * 參數二:密碼
         * 參數三:鹽值
         * 參數四:realm的名字
         */
        AuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user,user.getPassword(),source,realmName);
        return authenticationInfo;
    }
}

           

Controller

package com.shiro.login.controller;

import com.shiro.login.domain.User;
import com.shiro.login.service.UserService;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpSession;

/**
 * @Author: Adorez
 * @Date: 2019/11/27 17:57
 * @Description:
 */
@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/toLogin")
    public String login(User user, HttpSession session){
        //通過SecurityUtils.getSubject()可以獲得目前的Subject
        Subject subject = SecurityUtils.getSubject();
        //判斷是否有登入資訊
        if(!subject.isAuthenticated()){
            UsernamePasswordToken token=new UsernamePasswordToken(user.getName(),user.getPassword());
            try {
                //執行認證操作
                subject.login(token);
                session.setAttribute("user", subject.getPrincipal());//getPrincipal()得到目前登入的使用者名
            }catch (Exception e){
                System.out.println(e.getStackTrace());
                return "redirect:login";
            }
        }
        return "redirect:/";
    }
    @GetMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        if (subject!=null) {
            subject.logout();
        }
        return "login";
    }
}

           

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登入</title>
</head>
<body>
    <h1>使用者登入</h1>
    <hr>
    <form id="from" th:action="@{/toLogin}" method="post">
        <table>
            <tr>
                <td>使用者名</td>
                <td>
                    <input type="text" name="name" placeholder="請輸入賬戶名"/>
                </td>
            </tr>
            <tr>
                <td>密碼</td>
                <td>
                    <input type="password" name="password" placeholder="請輸入密碼"/>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <font color="red">[[${msg}]]</font>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="登入"/>
                    <input type="reset" value="重置"/>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>
           

index.html

<!DOCTYPE html>
<html xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <title>首頁</title>
</head>
<body>
    <h1>首頁</h1>
    <hr>
    <ul>
        <li><a href="/user/index">個人中心</a></li>
        <shiro:hasRole name="vip"><li><a href="/vip/index">會員中心</a></li></shiro:hasRole>
        <li><a href="/test/index">hei</a></li>
        <li><a href="logout">登出</a></li>
    </ul>
</body>
</html>
           

error/unAuth.html

SpringBoot整合Shiro權限管理SpringBoot整合Shiro權限管理shiro标簽的使用
<!DOCTYPE html>
<html>
<head>
    <title>未授權提示</title>
</head>
<body>
    <h1>您還不是<font color="red">會員</font>,沒有權限通路這個頁面!</h1>
</body>
</html>
           

shiro标簽的使用

guest标簽 驗證目前使用者是否為“訪客”,即未認證(包含未記住)的使用者
user标簽 認證通過或已記住的使用者
authenticated标簽 已認證通過的使用者。不包含已記住的使用者,這是與user标簽的差別所在
notAuthenticated 未認證通過使用者,與authenticated标簽相對應。與guest标簽的差別是,該标簽包含已記住使用者
principal 輸出目前使用者資訊,通常為登入帳号資訊
hasRole 驗證目前使用者是否屬于該角色
lacksRole 與hasRole标簽邏輯相反,當使用者不屬于該角色時驗證通過
hasAnyRole 驗證目前使用者是否屬于以下任意一個角色
hasPermission 驗證目前使用者是否擁有指定權限
lacksPermission 與hasPermission标簽邏輯相反,目前使用者沒有制定權限時,驗證通過

項目具體實作

連結:https://pan.baidu.com/s/1RKzOZFU81XgJJ2di7nFpCA

提取碼:qzp4

繼續閱讀