SpringBoot + Shiroがログイン認証を実現(実装プロセス+ソースコード分析+コード表示)

1。概要

1.1 SpringBoot

今日は、SpringBootを使ってShiroと連携してログイン認証を行うので、SpringBootは必須です。誰でもShiroを使えると思いますし、SpringBootも悪くないので、詳しくは触れませんが、Shiroを中心に紹介します。

1.2 Shiro

Shiroは、Javaセキュリティフレームワークです。強力で使いやすいです。SpringSecurityに比べて広く使用されています。Shiroは、強力な機能を維持しながら、シンプルさと柔軟性に大きな利点があります。
ここに画像の説明を挿入
Shiroの機能には以下も含まれます。

  • 認証:ID認証。ユーザーがIDを持っているかどうかを確認します。
  • 許可:許可の確認。認証されたユーザーが特定の許可を持っているかどうかを確認します。「誰」が「何」にアクセスできるかを決定します。
  • セッション管理:セッション管理、ユーザーがログインした後のセッションの管理、
  • 暗号化:暗号化。暗号化を使用して、暗号化されたパスワードなどのデータを暗号化します。
  • Webサポート:Webサポートは、Web環境に簡単に統合できます。
  • キャッシュ:ユーザーデータのキャッシュ、キャッシュ、
  • 並行性:並行性、Apache Shiroは、並行機能を持つマルチスレッドアプリケーションをサポートします。つまり、マルチスレッドアプリケーションで並行検証をサポートします。
  • テスト:テストのサポートを提供するテスト。
  • 実行:ユーザーが他のユーザーとしてログインできるようにします。
  • 覚えておいてください:覚えておいてください。その
    中でも、認証と承認の権限の確認は一緒に食べる方が良いです!
    今日は主にアイデンティティ認証を実現する方法について話します

2. Shiroがログイン認証を実現

ログイン認証にShiroを使用する前に、まずいくつかの概念的な問題を理解する必要があります。まず、優れたセキュリティフレームワークとして、Shiroには、後でカスタマイズするDefaultWebSecurityManagerクラスである中央管理センターが必要です。すべてのメソッドオブジェクトがこの管理センターに追加されます。この管理センターは、システムに付属するデフォルトの認証方法を置き換えるために定義するメソッドを制御します。次に、レルムも非常に重要なオブジェクトであり、Shiroとアプリケーションセキュリティデータ間の「ブリッジ」または「コネクタ」として機能する「ドメイン」に変換されます。カスタマイズするメソッドはAuthorizationRealm継承するクラスで記述してから、管理センターに追加する必要があります。最後の概念はSubjectです。通常は、Subjectオブジェクトをユーザーとして理解します。同様に、3者プログラムの場合もあります。これは抽象的な概念であり、システムとやり取りするすべての「もの」がSubjectであると理解できます。ログイン認証に使用します。

pomの依存関係をインポートする

まず、SpringBootプロジェクトを作成し、shiroとspringboot pomの依存関係をインポートします。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
                <!--shiro-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

設定後、設定ファイルapplication.ymlを設定し、内部にspringbootポート番号を設定します。

server:
  port: 8080  #自定义

次に、SpringBootスタートアップクラスを設定します。

@SpringBootApplication
public class ShiroApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ShiroApplication.class);
    }
}

コアメソッドを構成する

その後、コントローラーメソッドを構成してページとやり取りし、アカウントのパスワードを渡してログイン状況をテストし、ログインの前後にアクセス許可をテストします。

package com.df.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;


@RestController
public class LoginController {
    
    
    //如果需要使用shiro长期登陆,设置subject的rememberMe属性并且设置允许的范围为user。authc不允许被rememberMe用户访问。
    //这就是我们传入账号密码测试的地方
    @PostMapping(value = "/doLogin")
    public void doLogin(@RequestParam(value = "username") String username,
                        @RequestParam(value = "password") String password){
    
    
        Subject subject = SecurityUtils.getSubject();
        try {
    
    
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            subject.login(usernamePasswordToken);

            System.out.println("登陆成功");
        }catch (Exception e){
    
    
            e.printStackTrace();
            System.out.println("登陆失败");
        }
    }
	
    @RequestMapping(value = "/index")
    public String index(){
    
    
        System.out.println("欢迎来到主页");
        return "欢迎来到主页";
    }
    
    //我们可以使用postman进行调用测试 登录前后hello的区别
    @GetMapping(value = "/hello")
    public String hello(HttpServletRequest request){
    
    
        Cookie[] cookies = request.getCookies();
        System.out.println(cookies[0].getValue());
        return "hello";
    }
	//用来设置未登录用户跳转的方法
    @GetMapping(value = "/login")
    public String login(){
    
    
        return "Please Login !";
    }
	//注销方法
    @GetMapping(value = "/logout")
    public String logout(){
    
    
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        System.out.println("成功退出");
        return "success to logout";
    }
}

Controllerメソッドをセットアップしたら、レルムを作成してログインアカウント認証をセットアップします。構成ファイルを作成し、MyRealmクラスを設定してAuthorizingRealmを継承します。
ここに画像の説明を挿入
次に、認証プロセスを実装します。継承後、2つのメソッドを実装する必要があります。1つは認証メソッドで、もう1つは承認メソッドです。このセクションでは認証メソッドを完了するだけで済みます。

package com.df.config;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * @author Lin
 * @create 2020/7/15
 * @since 1.0.0
 * (功能):
 */
public class MyRealm extends AuthorizingRealm {
    
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
    
        return null;
    }

    @Override //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
    
       //从被shiro封装成的token中取出我们传入的username
        String username = (String) authenticationToken.getPrincipal();
		//这里应有一步去缓存或数据库查询的步骤,我省略了
        //我直接定义了一个username,如果用户名不匹配,则报错用户名不存在。
        if(!"LinJy".equals(username)){
    
    
            throw new UnknownAccountException("账号不存在");
        }
        //返回一个新封装的认证实体,传入的是用户名,数据库查出来的密码,和当前Realm的名字
        return new SimpleAuthenticationInfo(username, "123", this.getName());
    }
}

以上でアカウント認証が完了しましたが、アカウント認証後、パスワードをどうするかという質問がたくさんの友達にあるはずです。パスワードが一緒に認証されないのはなぜですか。認証パスワードを書き始めましょう。Shiroは確認のためにアカウントとパスワードを2か所に分割しますが、自分で定義しない場合は、shiroのデフォルトの確認メソッドが呼び出されます。SimpleCredentialsMatcher継承
するMyCredentialsMatcherクラスを再作成して、カスタムパスワード検証メソッドを実装します。

package com.df.config;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


public class MyCredentialsMatcher extends SimpleCredentialsMatcher {
    
    
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    
    
        UsernamePasswordToken tokenResolve = (UsernamePasswordToken) token;
        String tokenPwd = new String(tokenResolve.getPassword());
        String infoPwd =(String) info.getCredentials();
        //调用当前类重写的equals方法来对比两个password是否一致,返回对比结果
        return super.equals(tokenPwd, infoPwd);
    }
}

アカウントとパスワードを設定したら、アカウントとパスワードの確認コンポーネントをshiroに近づけるためにマネージャーを設定する必要があります。そうです、構成クラスが出てきました。構成クラスの主な機能は、以前に作成したいくつかのアクセサリを組み立て、ログインに必要なログイン要求をインターセプトするインターセプターをセットアップすることです。コードで直接言うことは多くありません。コードには特定のコメントがあります。

package com.df.config;


import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfig {
    
    
	//引入之前定义好的域
    @Bean
    MyRealm myRealm(){
    
    
        return new MyRealm();
    }
	//配置一个安全管理器
    @Bean
    DefaultWebSecurityManager securityManager(){
    
    
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        MyRealm myRealm = myRealm();
        //将我们配置好的密码校验放入域中
        myRealm.setCredentialsMatcher(myCredentialsMatcher());
        //将域添加到我们的安全管理器中
        manager.setRealm(myRealm);
        //设置Session管理器,配置shiro中Session的持续时间
        manager.setSessionManager(getDefaultWebSessionManager());

        return manager;
    }
	//引入密码校验
    @Bean
    public MyCredentialsMatcher myCredentialsMatcher(){
    
    
        return new MyCredentialsMatcher();
    }

    //设置session过期时间
    @Bean
    public DefaultWebSessionManager getDefaultWebSessionManager() {
    
    
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        defaultWebSessionManager.setGlobalSessionTimeout(1000 * 60);// 会话过期时间,单位:毫秒--->一分钟,用于测试
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
        defaultWebSessionManager.setSessionIdCookieEnabled(true);
        return defaultWebSessionManager;
    }



	//设置访问拦截器
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(){
    
    
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //传入安全管理器
        bean.setSecurityManager(securityManager());
        //传入未登录用户访问登陆用户的权限所跳转的页面
        bean.setLoginUrl("/login");

        //设置成功后返回页面
        bean.setSuccessUrl("/index");

        //访问未授权网页所跳转的页面
        bean.setUnauthorizedUrl("/unauthorized");
        Map<String, String> map = new LinkedHashMap<>();
        //允许  需要设置login为anon 否则登陆成功后无法成功跳转。
        map.put("/login", "anon");
        map.put("/doLogin", "anon");
        map.put("/index", "anon");
        //设置所有的请求未登录不允许进入。
        map.put("/**", "authc");
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

}

次に、SpringBootを試してみることができます。まず、/ helloなどのインターセプトされたリクエストにアクセスすることをお勧めします。アクセスできないことがわかります。次に、/ doLoginにログインしてから、helloにアクセスします。ソースコードをgithubに投稿します。アドレスリンクが下部にあり、dubbo + zookeeper統合shiroのコピーが添付されています。

3. Shiroログイン認証ソースコード分析

次に、shiroがアカウントとパスワードの検証を段階的に実装する方法を詳しく見ていきましょう。まず、おなじみの/ doLoginメソッドから始めます。
ここに画像の説明を挿入
入力後、shiroが入力したトークンをセキュリティマネージャーに渡して呼び出し
ここに画像の説明を挿入
たことがわかりました。セキュリティマネージャーに入った後、セキュリティマネージャーがログインメソッドを呼び出し、トークンを渡し、このメソッドを入力してRealmアカウントを作成したことがわかりました。認証。
ここに画像の説明を挿入
上記は、渡されたトークンに問題があるかどうかを判断するものであり、問​​題がない場合は、トピックを入力して認証メソッドを呼び出します。以下のエラーレポートから、アカウント認証の必要性を判断できることがわかります。
ここに画像の説明を挿入
次に、ここに来ます。this.assertRealmConfigured()の最初のステップは、Realmがnull値かどうかを判断してRealmを取得するRealmの判断です。以前にレルムを構成したため、唯一のレルムを呼び出します。それ以外の場合は、レルムをトラバースし、判断を1つずつ呼び出します。以下は、複数のレルムを呼び出すコードです。
ここに画像の説明を挿入
さらに詳しく見ていくと、まず、レルムがトークンをサポートしているかどうかを判断し、サポートしている場合は、認証のためにレルムを呼び出します。
ここに画像の説明を挿入
次に、レルムに入り、以前にログインしたキャッシュを取得します。空の場合は、認証プロセスが開始され
ここに画像の説明を挿入
ます。これは、慣れ親しんだ方法に戻ると考えることができ、ユーザー名を判断するために前に定義した方法です。判定が成功すると、データベースで見つかったユーザー名とパスワードが再パッケージ化されます。その後戻ります。
ここに画像の説明を挿入
アカウント認証を実行すると、画像に表示される情報情報が返されます。情報がnullでない場合、パスワードが検証されます。
ここに画像の説明を挿入
入力後、最初にCredentialsMatcherオブジェクトを取得しました。このオブジェクトについて、そのインターフェース実装クラスを見つけ、パスワード照合メソッドを実装したことを確認したので、カスタムオブジェクトを呼び出します。もちろん、カスタマイズしない場合は、デフォルトの暗号化パスワードが認証の比較に使用されます。
ここに画像の説明を挿入
ここに画像の説明を挿入
次に、下に進み、パスワード認証の段階に入ります。
ここに画像の説明を挿入
よく知っている場所に入り、2つのパスワードを確認しました。このクラスのメソッドを呼び出してパスワードを確認すると、実際には1文字のトラバーサル比較であり、正しくない場合はfalseが返され、
ここに画像の説明を挿入
最終的には終了します。花で終わりました〜プロジェクトのアドレスを下に記入してください、そしてそれを必要とする友人はそれを拾います。

コードアドレス

コードソースコードアドレス、必要に応じてピックアップしてください〜

おすすめ

転載: blog.csdn.net/qq_41762594/article/details/107361997