詳解Java springboot 整合Shiro框架

Shiro介紹

Shiro是一款安全框架,主要的三個類Subject、SecurityManager、Realm

  • Subject:表示當前用戶
  • SecurityManager:安全管理器,即所有與安全有關的操作都會與SecurityManager交互;且其管理著所有Subject;可以看出它是Shiro的核心,它負責與Shiro的其他組件進行交互,它相當於SpringMVC中DispatcherServlet的角色
  • Realm:Shiro從Realm 獲取安全數據(如用戶、角色、權限)

Shiro框架結構圖

Springboot整合Shiro

建項目是勾選spring web,導入依賴

<!--        thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
<!--        shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>
<!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.2</version>
        </dependency>
<!--        mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
<!--        druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
<!--        mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
<!--        log4j-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
<!--        thymeleaf、shiro整合包-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

編寫頁面及其控制層

 轉發的設置,全部編寫在MVCConfig中的前端控制器中

@Configurationpublic class MyMvcConfig implements WebMvcConfigurer {    @Override    public void addViewControllers(ViewControllerRegistry registry) {        registry.addViewController("/").setViewName("index");        registry.addViewController("/login.html").setViewName("login");        registry.addViewController("/user/add").setViewName("user/add");        registry.addViewController("/user/update").setViewName("user/update");        registry.addViewController("/loginout").setViewName("login");    }}

連接數據庫

編寫application.yml

spring:
  datasource:
    username: ***
    password: ***
    url: jdbc:mysql://localhost:3306/db_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
   
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
  type-aliases-package: com.example.demo.pojo
 

編寫 pojo、dao、service三層,dao層可以直接使Mybatis的註解。

需要的方法就是findByName(String username),通過表單傳入的username值進行查詢。

編寫UserRealm 需要繼承AuthorizingRealm

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private IuserService iuserService;
//    授權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("===>授權");
        SimpleAuthorizationInfo Info = new SimpleAuthorizationInfo();
        //        獲取登錄對象
        Subject subject = SecurityUtils.getSubject();
        user principal = (user) subject.getPrincipal();//拿到user
        Info.addStringPermission(principal.getPerms());
        return Info;
    }
//    認證
 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("==>認證");
        UsernamePasswordToken authenticationToken1 = (UsernamePasswordToken) authenticationToken;
        user byName = iuserService.findByName(authenticationToken1.getUsername());
        if(byName==null){
            return null;//拋出用戶名錯誤的異常
        }
        //密碼認證shiro自己完成  將user對象 傳遞給上面的方法進行授權
        return new SimpleAuthenticationInfo(byName,byName.getPassword(),"");
    }
}

代碼的分析:

認證部分:

將表單提交的數據封裝成一個對象,通過username從數據庫中查詢返回一個對象,進行比對

最後將這個查詢的對象傳遞給授權方法。

授權部分:

獲取到用戶對象,給用戶對象進行相應的授權。(傳遞的user對象中就有權限設置)

編寫ShiroConfig

@Configuration
public class ShiroConfig {
    @Bean   //創建對象
    public UserRealm userRealm(){
        return new UserRealm();
    }
    @Bean   //接管對象  @Bean 默認使用方法名稱
    public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    @Bean  //交給前端處理
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
 
        HashMap<String, String> hashMap = new HashMap<>();
//        該路徑 必須通過認證 才能進行訪問
        hashMap.put("/user/*","authc");
//        進行授權
        hashMap.put("/user/add","perms[add]");
        hashMap.put("/user/update","perms[update]");
//        註銷
        hashMap.put("/logout","logout");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);
//        設置登錄頁面的路徑
        shiroFilterFactoryBean.setLoginUrl("/login.html");
//        設置授權頁面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noLogin");
        return shiroFilterFactoryBean;
    }
 
//    完成整合
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}

代碼分析

在這個配置類中,配置的方法就是ioc註入。

ShiroFilterFactoryBean中可以配置

  • 資源路徑對應的權限
  • 登陸頁面
  • 權限不足 無法訪問的頁面路徑
  • 註銷

補充: 攔截的屬性

  • anon: 無需認證就可以訪問
  • authc: 必須認證瞭才能訪問
  • user: 必須擁有記住我功能才能用
  • perms: 擁有對某個資源的權限才能訪問
  • role: 擁有某個角色權限

編寫控制層代碼

@Controller
public class logincontroller {
//    執行流程 前端表單-》控制層代碼-》config
    @PostMapping("/login")
    public String login(String username, String password, Model model){
//        獲取一個用戶
        Subject subject = SecurityUtils.getSubject();
//        封裝用戶登陸數據
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
//        執行登錄方法,如果失敗就會拋出異常
        try{
            subject.login(usernamePasswordToken);
            return "index";
        }catch (UnknownAccountException e){
            model.addAttribute("msg","用戶名錯誤");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密碼錯誤");
            return "login";
        }
    }
    @GetMapping("/noLogin")
    @ResponseBody
    public String nologin(){return "未經授權 無法訪問";}
 
}

代碼分析:

login方法:獲取從表單傳遞的數據,封裝從UsernamePasswordToken對象,調用login方法進行登錄操作

Shiro整合Thymeleaf

在ShiroConfig需要整合ShiroDialect

//    完成整合
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

約束

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

使用方法

  • shiro:notAuthenticated:沒有進行登錄 顯示
  • shiro:authenticated:已經登陸 顯示
  • shiro:hasPermission="A"  用戶存在A的權限則顯示

示例代碼:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首頁</h1>
<div shiro:notAuthenticated>
    <a th:href="@{/login.html}">登錄</a>
</div>
<div shiro:authenticated>
    <a th:href="@{/logout}">註銷</a>
</div>
<div shiro:hasPermission="add">
    <a th:href="@{/user/add}">ADD</a>
</div>
<div shiro:hasPermission="update">
    <a th:href="@{/user/update}">UPDATE</a>
</div>
 
</body>
</html>

總結

登錄的流程:login表單-》loginController-》ShiroConfig-》UserRealm

效果:

點擊登錄,控制臺會顯示

 進入add/update的頁面,也會打印"===>授權",這個也證明瞭登錄的執行流程

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: