Shiro は SpringBoot と統合して、ログインインターセプト、ユーザー認証、ユーザー認可などを実装します。実践デモ

最初に必ずお読みください:

hiro フレームワークを学ぶのは難しくありませんが、基礎となるカプセル化は非常に奥深く、ブログで説明することはできません。これについては、おそらくさらにいくつかの記事が必要になるでしょう。この記事は主に hiro のプロジェクト構造の設定と基本的なものについてです。シロの機能です。このブログで構築したプロジェクトをベースに、これからシロを少し勉強しようと思ったら、理解するか使いこなせるか、手でコードを書く必要があります。キーボードを打つのは苦手ですが、給料は1万元未満です。後ほど更新していきますので、全部読んで確実にシロが身につくとは保証できませんが、必ず得るものはあると思います。
に固執します!

1. 開発環境

名前 バージョン
その考えはわかります 2019.3.5 x64
JDK 1.8
MySQL mysql-5.7.31-winx64
スプリングブーツ 2.0以上
メイビン apache-maven-3.6.3
しろ 1.4.0
マイバティス 2.1.0

2. プロジェクトの構築

1. SpringBoot プロジェクトを作成し、関連する pom 依存関係をインポートします。

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
        <relativePath/><!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.1.3.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. リソース内に application.yml 構成ファイルを作成します。
詳細な設定は次のとおりです:
アイデアが古いバージョンの場合、システムが MyBatis のマッパー ディレクトリを見つけることができないため、エラーが報告される場合があります。リソース ディレクトリにマッパー フォルダーを作成するか、一時的に MyBatis を無効にすることができます。 yml 設定ファイル内の関連設定 (コメントアウトの意味は、設定ファイル内のコメントに # 記号が使用されることです)。

server:
  port: 8080
spring:
  application:
    name: shiro-springboot
  datasource:
    url: jdbc:mysql://localhost:3306/hibernate
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.xa.DruidXADataSource
  thymeleaf:
    cache: false
  mvc:
    static-path-pattern: /templates/user/**
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.zkr.mingyu.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    com.example.demo.dao : debug

SpringBoot でホット デプロイメントを有効にする方法は次のとおりです。私の他のブログの紹介を参照してください。
航空チケット: SpringBoot プロジェクトのホット デプロイメントを有効にする方法

3. SpringBoot スターター Application.class:
ここに画像の説明を挿入します
スタートアップ クラスのソース コードを作成します。

package com.zkr.mingyu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }

}

プロジェクトが正常に開始できるかどうかをテストするには、localhost:8080 ポートにアクセスして、プロジェクトが正常に実行されるかどうかを確認します。
ここに画像の説明を挿入します

3. hiro構成クラスを構成する

現在のプロジェクト ディレクトリ:
ここに画像の説明を挿入します

SpringBoot と統合するための構成クラスを作成します。
設定クラスの詳細は以下のとおりです。
下から順に説明します。
ページ内で hiro タグが使用されていない場合は、Shiro と Thymeleaf の統合設定クラスを記述する必要はありません。

  1. カスタムレルム。
  2. セキュリティマネージャー DefaultWebSecuityManager を作成します。
  3. CreateShiroFilterFactoryBean
  4. hiro は、構成クラスを Thymeleaf と統合します。ページが hiro タグをサポートするため)。

この記事全体の中で最も重要かつ重要な 2 つのポイントは、Realm および hiro 構成クラスのカスタマイズです。全文コア

1. Realm クラスをカスタマイズします。

(1) MyRealm クラスを作成し、継承する必要がある AuthorizingRealm クラスを継承します。
(2) doGetAuthorizationInfo メソッドと doGetAuthenticationInfo メソッドを実装します。
ログインを実現するには、doGetAuthenticationInfo が核となり、doGetAuthenticationInfo() メソッドでアカウントのパスワードが検証されます。
ページのリソース制限、ユーザーがアクセスできるリソース、実行できる操作などの承認操作は doGetAuthorizationInfo() メソッドで実行されます。すべてこの方法で設定されます。

package com.zkr.mingyu.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.io.Serializable;

public class MyRealm extends AuthorizingRealm implements Serializable {

    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行授权方法: doGetAuthorizationInfo");
        return null;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行认证方法: doGetAuthenticationInfo");
        return null;
    }
}

2. hiro 構成クラスを作成します。

この設定クラスは非常に重要なので、よく読んでリンクごとにリンクしてください。その後のパスワード暗号化(MD5 + ソルト + ハッシュ)はこのクラスで設定され、Thymeleaf などとの統合設定も行われます。

package com.zkr.mingyu.config;

import com.zkr.mingyu.shiro.MyRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**标注 @Configuration 注解,
 * 标注这是一个配置类,
 * 让项目启动时加载该配置类。
 */
@Configuration
public class ShiroConfig {

    /**
     * 创建ShiroFilterFactory
     * 设置权限规则 需要注入securityManage
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建安全管理器,
     * 并为 securityManager 注入自定义的 Realm 类
     * @param realm
     * @return
     */
    @Bean
    public DefaultWebSecurityManager getSecurityManager(MyRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 配置自定义 Realm 类
     * @Bean 将 MyRealm 注入到 Spring 容器当中
     * @return
     */
    @Bean
    public MyRealm getMyRealm(){
        return new MyRealm();
    }

}

4. ページ、コントローラー、サービスなどを作成します。

1. HTML ページを保存するためのテンプレート フォルダーをリソース ディレクトリに作成します。そしてインデックスページとログインページを作成します。
インデックス:
サイトのホームページ
ログイン:
ログインページ

2. コントローラー、サービス、dao など
現在のディレクトリ構造:
ここに画像の説明を挿入します
コントローラー:

package com.zkr.mingyu.controller;

import com.zkr.mingyu.entity.User;
import com.zkr.mingyu.service.UserService;
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 javax.annotation.Resource;

@Controller
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("/index")
    public String login(User user, Model model) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());

        try {
            subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg", "用户名错误!");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "密码错误!");
            return "login";
        }

    }

    @RequestMapping("/login")
    public String index() {
        return "login";
    }

    @RequestMapping("/loginOut")
    public String loginOut() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "login";
    }

    @RequestMapping("/update")
    public String update() {
        return "/user/update";
    }

}

サービス:

package com.zkr.mingyu.service;

import com.zkr.mingyu.entity.User;
import org.apache.ibatis.annotations.Param;

public interface UserService {

    /**
     * 根据用户名查找用户
     * @param userName
     * @return
     */
    User findByUserName(@Param("username") String userName);

}

サービスの実装:

package com.zkr.mingyu.service;

import com.zkr.mingyu.dao.UserMapper;
import com.zkr.mingyu.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
@Transactional //开启事务
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public User findByUserName(String userName) {
        return userMapper.findByUserName(userName);
    }
}

ダオ:

package com.zkr.mingyu.dao;

import com.zkr.mingyu.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {

    /**
     * 根据用户名查找用户
     * @param userName
     * @return
     */
    User findByUserName(@Param("username") String userName);

}

実在物:

package com.zkr.mingyu.entity;

import java.io.Serializable;

public class User implements Serializable {

    /**
     * id
     */
    private Integer id;
    /**
     * 账号
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 权限
     */
    private String auth;
    /**
     * 随机盐
     */
    private String salt;

    public User() {
    }

    public User(Integer id, String username, String password, String auth, String salt) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.auth = auth;
        this.salt = salt;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getAuth() {
        return auth;
    }

    public void setAuth(String auth) {
        this.auth = auth;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", auth='" + auth + '\'' +
                ", salt='" + salt + '\'' +
                '}';
    }
    
}

マッパー:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zkr.mingyu.dao.UserMapper">
    <select id="findByUserName" parameterType="String" resultType="user">
        select id, username, password from user where username = #{username}
    </select>
</mapper>

5. カスタマイズされた MyRealm を変更します

カスタマイズされた MyRealm クラスの doGetAuthenticationInfo() メソッドは、主にユーザー認証を構成します。
doGetAuthorizationInfo() メソッドは主にユーザー権限の設定を行いますが、端的に言えば、このユーザーにどのような役割と権限が割り当てられているかを意味します。また、特定のリソースにアクセスできるかどうか、アクセスできる場合、ユーザーがそのリソースに対してどのような操作を実行できるか。操作 == CRUD。

package com.zkr.mingyu.shiro;

import com.zkr.mingyu.entity.User;
import com.zkr.mingyu.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.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;

public class MyRealm extends AuthorizingRealm implements Serializable {

    @Autowired
    private UserService userService;

    /**
     * 执行授权逻辑
     * 权限要和资源对应
     * 权限声明该用户可以访问系统中哪些资源,对系统中哪些资源进行操作
     * 不同的用户,拥有不同的权限
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行授权方法: doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        User byUserName = userService.findByUserName(user.getUsername());
        /**
         * 这个方法中为授权操作
         * 基本常用方法有:
         */

        /**
         * 控制台打印结果:
         * getRoles: null
         * getObjectPermissions: null
         * getStringPermissions: [user:add]
         * getClass: class org.apache.shiro.authz.SimpleAuthorizationInfo
         */

        //获取用户角色
        /* System.out.println("getRoles: " + info.getRoles());
        System.out.println("getObjectPermissions: " + info.getObjectPermissions());
        //获取用户权限
        System.out.println("getStringPermissions: " + info.getStringPermissions());
        System.out.println("getClass: " + info.getClass());*/
        return null;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行认证方法: doGetAuthenticationInfo");
        String username = (String) token.getPrincipal();
        User user = userService.findByUserName(username);
        if(user == null){
            return null;
        }
        return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
    }
}

これは私が作成したもう 1 つの MyRealm 構成です。見て理解してください。後日更新します このブログの冒頭でも述べたように、プロジェクトの基本的な枠組みを構築することです。後で記事の最後に新しいブログのリンクを追加します。最近お時間がありましたら、この知識を共有していただきありがとうございます。
ここに画像の説明を挿入します

6. リソースのアクセス権限を設定するShiroConfig

ShiroFilterFactoryBean メソッドは主に、特定のリソースにアクセスするために必要な権限を構成します。そしてデフォルトのログインページのURLアドレスを設定します。権限が不十分な特定のリソースにアクセスした場合に、どのページにジャンプするかをここで設定します。
DefaultWebSecurityManager はセキュリティ マネージャーです。後でパスワード暗号化操作 (パスワード + ソルト + MD5 + ハッシュ) を実行するときは、DefaultWebSecurityManager() メソッドでカスタム パスワード マネージャーを構成する必要があります。

package com.zkr.mingyu.config;

import com.zkr.mingyu.shiro.MyRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**标注 @Configuration 注解,
 * 标注这是一个配置类,
 * 让项目启动时加载该配置类。
 */
@Configuration
public class ShiroConfig {

    /**
     * 创建ShiroFilterFactory
     * 设置权限规则 需要注入securityManage
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        /**
         * Shiro内置过滤器,可实现权限相关的拦截器
         *      常用的过滤器:
         *      anon: 无需认证(登录) 可以访问
         *      authc: 必须认证才可以访问
         *      user:如果使用rememberMe的功能可以直接访问
         *      perms: 该资源必须得到资源权限才可以访问
         *      role: 该资源必须得到角色权限才可以访问
         */
        Map<String, String> filterMap = new LinkedHashMap<String,String>();
        filterMap.put("/login","anon");
        filterMap.put("/index","anon");
        filterMap.put("/*","authc");
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建安全管理器,
     * 并为 securityManager 注入自定义的 Realm 类
     * @param realm
     * @return
     */
    @Bean
    public DefaultWebSecurityManager getSecurityManager(MyRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 配置自定义 Realm 类
     * @Bean 将 MyRealm 注入到 Spring 容器当中
     * @return
     */
    @Bean
    public MyRealm getMyRealm(){
        return new MyRealm();
    }

}

コードはそれほど多くなく、デモを書いて後で少しずつ改良していくだけなので、時間があるときに新しい記事を更新していきます。たとえば、パスワード暗号化、リソース権限のセグメント化、セッション、キャッシュなどです。
後で新しい関数を書いたら、記事の最初と最後に配置し、リンクを貼り付けます。読んだらいいねをお願いします♪ いいねが更新の励みになります!記事を収集すると、後ほど記事の先頭に新しいブログへのリンクが表示されます。

添付の文章は(お互いを励ますため):
沸騰したお湯の中では反射は見えず、怒りでは真実が見えません。

おすすめ

転載: blog.csdn.net/uziuzi669/article/details/108420499