Changbuモール(9):Spring Security Oauth2

一生懸命勉強し、毎日改善する

この記事は私のGithubリポジトリに含まれていますDayDayUP:github.com/RobodLee/DayDayUP、Starへようこそ。その他の記事については、ディレクトリナビゲーションにアクセスしてください。

序文

私は以前にSpringSecurityとOAuth2.0を学んだことがなかったので、この章のビデオを見ると混乱しました。そのため、この知識を補うのに数日かかり、これら2つの部分を詳細に説明する2つの記事を書きました。

以下の内容は本プロジェクトのみであり、最初の2つの記事に記載されている内容は記載されていません。

認定サービス紹介

OAuth2.0の構築方法前回の記事で詳しく説明しましたが、ここでは、情報で提供されているコードを直接インポートできます。

各ファイルの役割の概要は次のとおりです。

  • AuthorizationServerConfig

    これは、OAuth2.0の認証サービス構成です。主なポイントは3つあります。1つ目は、クライアント情報構成です。つまり、クライアントIDやクライアントキーなど、クライアントがサーバーにアクセスするために必要な条件です。これらは、メモリ内で直接構成することも、データベースから読み取ることもできます。 2つ目は、認証マネージャー、トークンの保存方法などを構成するための認証サーバーエンドポイント構成です。3つ目は検証トークンの構成の制限など、アクセス制限を構成するための認証サーバーのセキュリティ構成です

  • CustomUserAuthenticationConverter

    カスタムUserAuthenticationConverterは、DefaultUserAuthenticationConverterを継承し、convertUserAuthenticationメソッドをオーバーライドしますデフォルトの方法は認証でユーザー名権限情報を取得することです。そして、この方法では、我々は、我々はまた、取得、書き換えられた元本をするために、認証にそれが私たちのカスタムであるかどうかを判断UserJwt。ない場合は、コールuserDetailsS​​ervice.loadUserByUsernameはそれを取得して、取得するには名前でIDをuserJwt返されたマップに追加します。に。

  • UserDetailsServiceImpl

    これは、UserDetailsS​​erviceインターフェイスを実装し、内部にloadUserByUsername()メソッドを実装するカスタム認証および承認クラスですこの方法は、フロントエンドから渡されたユーザー名に基づいて、対応するユーザー情報を見つけることです。次に、それを後続のフィルターに渡して、ユーザーIDを確認します。通常、この方法はデータベースからユーザーを検索する方法ですが、テスト目的では、一時ユーザーは直接新規であり、パスワードは「robod666」です。フロントエンドから渡されるパスワードが「robod666」である限り、通常どおりログインできます。

  • WebSecurityConfig

    これは、SpringSecurityのセキュリティ構成クラスです。これは主に、特定の要求に対して特定の制限付きで構成されます。このクラスでは、passwordEncoderauthenticationManagerBean、他のクラスが使用できるようにSpringコンテナーに挿入されます。

  • UserLoginController

    これは、ユーザー名とパスワードのみを使用してログインする、自己定義の簡略化されたログイン方法です。

  • LoginServiceLoginServiceImpl

    UserLoginControllerのサービスレイヤー。いくつかの必要な情報を追加し、RestTemplateシミュレーションブラウザを介してトークン情報を取得するようにサーバーに要求を送信する責任があります。

  • AuthToken

    トークン関連の情報をカプセル化します。トークン情報、更新トークン、jwtショートトークン。

  • CookieUtil

    クッキーツール。Cookieを設定し、名前に基づいてCookie情報を取得します。

  • UserJwt

    ユーザー情報。UserDetailsインターフェースを実装しましたこのクラスのオブジェクトは、ユーザーを認証するときに使用されます。

  • changgou.jks

    キー証明書は、keytoolツールを使用して生成できます。

  • application.yml

    認証サービス構成ファイル

    …………
    # 配置信息,给UserLoginController用的
    auth:
      ttl: 3600  #token存储到redis的过期时间
      clientId: changgou
      clientSecret: changgou
      cookieDomain: localhost
      cookieMaxAge: -1
    
    # 因为采用了非对称加密,所以这里配置了密钥的相关信息
    encrypt:
      key-store:
        location: classpath:/robod666.jks
        secret: robod666
        alias: robod666
        password: robod666
        …………
    

これらのファイルの役割はここで紹介されています。

非対称暗号化認証

認定プロセス分析

これは従来の認証プロセスです。トークンを持ってリソースサーバーにアクセスすると、リソースサービスはトークンを承認サービスに送信して、トークンが有効であることを確認します。これを行うと、サーバー間の相互作用がもう1つあり、非効率的であるため、サーバーの圧力が目に見えないほど増加します。

効率を上げるために、公開鍵秘密鍵検証方式を採用しています。

承認サービスは秘密鍵を使用してトークンを生成し、クライアントはトークンを運び、リソースサーバーに要求を送信します。リソースサーバーは公開キーを使用してトークンを検証し、検証に合格した後、次の手順に進みます。承認サービスとのやり取りを減らします。

公開鍵はどのサーバーにも保存できますが、秘密鍵は認証サービスにのみ保存できます。秘密鍵を取得した後、トークンを偽造できるため、セキュリティが低下します。したがって、非対称暗号化はセキュリティを向上させるためにも使用されます。

キー証明書を生成する

keytoolツールを使用してキーペアを生成できます。準備したフォルダで、コマンドラインウィンドウを開き、以下を実行します。

keytool -genkeypair -alias robod666 -keyalg RSA -keypass robod666 -keystore robod666.jks -storepass robod666
# -alias:密钥的别名 
# -keyalg:使用的hash算法 
# -keypass:密钥的访问密码
# -keystore:密钥库文件名,xc.keystore保存了生成的证书 
# -storepass:密钥库的访问密码 

次に、インターフェースにいくつかの質問があり、答えを入力し、最後に「y」を入力してキーを生成します。

生成されたキー証明書を認証サービスのresourcesディレクトリに配置します

公開鍵を抽出する

opensslをインストールした後、キー証明書が配置されているディレクトリでコマンドラインウィンドウを開き、実行します。

keytool -list -rfc --keystore robod666.jks | openssl x509 -inform pem -pubkey

このようにして、公開鍵を抽出できます。公開鍵を使用する必要があるマイクロサービスのresourcesディレクトリにpublic.keyファイルを作成し、このコンテンツを1行にマージして、に貼り付けます。

トークンを作成して解析する

public class JWTTest {
    
    

    //创建令牌
    @Test
    public void createJWT() {
    
    
        ClassPathResource classPathResource = new ClassPathResource("robod666.jks");
        KeyStoreKeyFactory keyStoreKeyFactory = 
            new KeyStoreKeyFactory(classPathResource, "robod666".toCharArray());
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("robod666");
        PrivateKey privateKey = keyPair.getPrivate();
        Map<String, Object> tokenMap = new HashMap<>();
        tokenMap.put("id", "1");
        tokenMap.put("name", "robod");
        tokenMap.put("roles", "ROLE_VIP,ROLE_USER");
        Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner((RSAPrivateKey) privateKey));
        System.out.println(jwt.getEncoded());
    }

    //解析令牌
    @Test
    public void parseJWT() {
    
    
        String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlcyI6IlJPTEVfVklQLFJPTEVfVVNFUiIsIm5hbWUiOiJyb2JvZCIsImlkIjoiMSJ9.KkPXXlYTkDCWq2VN5qy6w6FI5TgCbIy-GkQShaYGbfvcZsj0165XsgXSx7Sf5aUpGB-494ds-ZnLxs3oVMZ7_tbu-is1-gZOeQ0G1GLla0ytNkImXabnujgWH2B4bmX4lBLK7d8xTEQ4WoAnydWUusCmPjQDgdFZGHmccJLuYKqQPzru-4go1mFgjEeB7gNu6cLYyQc79bZdF2Mk2OX1Nxpb88sux0QkNAlb1-JuUhmjbUYwMK5l5W5zeNckRJtGy_Zy7OTwXviuRp6uISmWD7p1HYbkKH-ROKCgSu1cqnok0645Uou7Y54Nd8NosIqShuNYbBBo_BHWuyx_lKdk1g";
        String publicKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiq6KfbXc/viuB6oQ/80cfLSFIr7pX3PmteAQ2/dA+ReMLgULJb+U8Dax3xNpBgLAp+Ei2IMkBFJlJRn/iaYi5eMnCY2vyfHkC69x6OhhCtzWBRxGJkPRjLDU+Obhak2MrDI4zIpzQs2/phjqWXuEPMz7KMd5UhoAFZWLTW1Ih3CP962fuJdV83hj/2uWN/yaAgaLRxRlTw7HHoIEy1dX9prAnqQ/rOl2Igvwi23GNnzMrqlvR9qt1gBI+noHtMv07hkavUT1nmoYnt/pw2+FLMLFEun2gR3DUmqu79QC6trDf3cVfKyRP9A7TBjUEv+Ecrh8JQosQa8GongTzHhmOwIDAQAB-----END PUBLIC KEY-----";
        Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publicKey));
        String claims = jwt.getClaims();
        System.out.println(claims);
    }
}

トークンを作成すると、次のjava.lang.IllegalStateExceptionエラーが発生する可能性があります

アイデアを再開するだけです。

createJWT()を正常に実行すると、次のコンテンツが表示されます。

これは私たちが作成したトークンです。

parseJWT()を実行します。操作が成功した場合は、公開キーを使用してトークンの内容を解析できます。

アカウントパスワードログイン

ログインするときに、ユーザー名とパスワードに加えて、clientIdなどの他の情報も指定する必要があります。アカウントパスワードのみでログインできますか?事前にサーバーで他の情報を指定するだけです。フロントエンドがユーザー名とパスワードを渡し、サーバーが他の情報を追加して認証サービスに送信してログインし、トークンを取得してフロントエンドに返すという考え方です。

@Service
public class LoginServiceImpl implements LoginService {
    
    

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Override
    public Map login(String username, String password, String clientId, String clientSecret, String grantType) {
    
    
        String url = loadBalancerClient.choose("user-auth").getUri() +
                "/oauth/token";
        //请求体
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("username", username);
        body.add("password", password);
        body.add("grant_type", grantType);
        //请求头
        MultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String authorization = "Basic " +
                new String(Base64.getEncoder().encode((clientId + ":" + clientSecret).getBytes()));
        header.add("Authorization", authorization);
        HttpEntity httpEntity = new HttpEntity(body, header);
        ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        return response.getBody();
    }

}
@Value("${auth.clientId}")
private String clientId;

@Value("${auth.clientSecret}")
private String clientSecret;

//密码模式  认证.
@RequestMapping("/login")
public Result<Map> login(String username, String password) throws Exception {
    
    
    String grantType = "password";
    Map token = loginService.login(username, password, clientId, clientSecret, grantType);
    return new Result<>(true, StatusCode.OK,"令牌生成成功",token);
}

これで、アカウントパスワードのみでトークンを申請できます。

総括する

この記事では、主にOAuth2.0認証サービスのインポート方法について説明します。理解できない場合でも、理解するのは非常に難しいため、最初に前の記事に進むことができます。インポート後、各ファイルの役割について簡単に説明します。前書き。次に、非対称暗号化を使用するときに公開鍵と秘密鍵を生成する方法について説明しました。最後に、アカウントとパスワードだけでログインする方法について話しました。

私の記事が役に立ったら、親指を立てる収集する転送する注意を払うことを忘れないでください良いコメントがあれば、下にメッセージを残してください。次回お会いしましょう!

WeChatパブリックアカウント

おすすめ

転載: blog.csdn.net/weixin_43461520/article/details/108144023