SpringBoot + Shiro는 로그인 인증을 실현합니다 (구현 프로세스 + 소스 코드 분석 + 코드 표시).

1. 개요

1.1 SpringBoot

오늘 우리가해야 할 일은 로그인 인증을 위해 SpringBoot를 사용하여 Shiro와 협력하는 것이므로 SpringBoot는 필수 불가결합니다. 모든 사람이 Shiro를 사용할 수 있고 SpringBoot가 나쁘지 않아야하므로 너무 자세하게 설명하지 않고 Shiro를 주로 소개합니다.

1.2 Shiro

Shiro는 Java 보안 프레임 워크로 강력하고 사용하기 쉬우 며 Spring Security에 비해 더 널리 사용되고 있으며 Shiro는 강력한 기능을 유지하면서 단순성과 유연성에서 큰 장점을 가지고 있습니다.
여기에 사진 설명 삽입
Shiro의 기능은 다음과 같습니다.

  • 인증 : 사용자가 신원을 가지고 있는지 확인하기위한 신원 인증.
  • 인증 : 인증 된 사용자에게 특정 권한이 있는지 확인하기위한 권한 확인입니다. "누가" "무엇"에 액세스 할 수 있는지 확인합니다.
  • 세션 관리 : 세션 관리, 사용자 로그인 후 세션 관리,
  • 암호화 : 암호화, 암호화 된 암호와 같은 데이터를 암호화하기 위해 암호화를 사용합니다.
  • 웹 지원 : 웹 지원은 웹 환경에 쉽게 통합 될 수 있습니다.
  • 캐싱 : 캐싱, 사용자 데이터 캐싱,
  • 동시성 : 동시성, Apache Shiro는 동시 기능이있는 다중 스레드 애플리케이션을 지원합니다. 즉, 다중 스레드 애플리케이션에서 동시 검증을 지원합니다.
  • 테스트 : 테스트 지원을 제공하는 테스트.
  • 다음 계정으로 실행 : 사용자가 다른 사용자로 로그인하도록 허용합니다.
  • 저를 기억하십시오 : 저를 기억하십시오.
    그중 인증 및 승인 권한 확인이 함께 먹는 것이 더 좋습니다!
    오늘 우리는 주로 신원 인증을 달성하는 방법에 대해 이야기합니다.

2. Shiro는 로그인 인증을 실현합니다.

로그인 인증에 Shiro를 사용하기 전에 먼저 몇 가지 개념적 문제를 이해해야합니다. 첫째, 우수한 보안 프레임 워크 인 Shiro에는 중앙 관리 센터가 있어야합니다. 즉 , 나중에 사용자 정의 할 DefaultWebSecurityManager 클래스입니다. 모든 메서드 개체가이 관리 센터에 추가되어 시스템과 함께 제공되는 기본 인증 방법을 대체하기 위해 정의한 메서드를 제어합니다. 둘째, Realm 은 또한 매우 중요한 객체로, Shiro와 애플리케이션 보안 데이터 간의 "브리지"또는 "커넥터"역할을하는 "도메인"으로 번역됩니다. 사용자 지정하는 메서드는 관리 센터에 추가하기 전에 AuthorizingRealm상속 하는 클래스에 작성되어야 합니다. 마지막 개념은 주제 입니다. 일반적으로 주제 객체를 사용자로 이해합니다. 마찬가지로 타사 프로그램 일 수도 있습니다. 추상 개념이며 시스템과 상호 작용하는 모든 "사물"이 주제 인 것으로 이해 될 수 있습니다. 로그인 인증에 사용합니다.

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);
    }
}

핵심 방법 구성

그런 다음 페이지와 인터페이스하고 계정 암호를 전달하여 로그인 상황을 테스트하고 로그인 전후에 권한을 테스트하는 Controller 메서드를 구성합니다.

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 메소드를 설정 한 후 Realm을 생성하여 로그인 계정 인증을 설정합니다. 구성 파일을 만들고 AuthorizingRealm 을 상속하도록 MyRealm 클래스를 설정합니다 .
여기에 사진 설명 삽입
그런 다음 인증 프로세스를 구현할 것입니다. 상속 후 두 가지 방법을 구현해야합니다. 하나는 인증 방법이고 다른 하나는 권한 부여 방법입니다.이 섹션에서는 인증 방법 만 완료하면됩니다.

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는 인증을 위해 계정과 비밀번호를 두 군데로 나눠서 스스로 정의하지 않으면 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 메소드로 시작해야합니다.
여기에 사진 설명 삽입
입력 후 시로가 입력 한 토큰을 보안 관리자에게을 (를) 호출하기 위해 준 것을 발견했습니다
여기에 사진 설명 삽입
. 보안 관리자를 입력 한 후 보안 관리자가 로그인 메소드를 호출하고 토큰을 전달하고이 메소드를 입력하여 Realm 계정을 만드는 것을 발견했습니다. 인증.
여기에 사진 설명 삽입
위는 우리가 전달한 토큰이 문제가 있는지 판단하기위한 것입니다. 그렇지 않다면 주제를 입력하고 인증 방법을 호출합니다. 아래 오류 보고서를 보면 계정 인증이 필요한지 판단 할 수 있음을 알 수 있습니다.
여기에 사진 설명 삽입
그런 다음 여기로 오세요. this.assertRealmConfigured ()의 첫 번째 단계는 Realm에 대한 판단으로 Realm이 null 값인지 판단한 다음 Realm을 가져옵니다. 이전에 Realm을 구성했기 때문에 유일한 Realm을 호출하고 그렇지 않으면 Realm을 횡단 한 다음 하나씩 판단을 호출합니다. 다음은 여러 Realm을 호출하는 코드입니다.
여기에 사진 설명 삽입
더 자세히 살펴보면 먼저 영역이 토큰을 지원하는지 여부를 판단하고 지원하는 경우 인증을 위해 영역을 호출하는 것을 볼 수 있습니다.
여기에 사진 설명 삽입
그런 다음 영역을 입력하여 이전에 로그인 한 캐시를 얻습니다. 비어 있으면 인증 프로세스가 시작됩니다
여기에 사진 설명 삽입
. 우리가 익숙한 방법으로 되돌아가 볼 수 있는데, 이는 사용자 이름을 판단하기 위해 이전에 정의한 방법입니다. 판단이 성공하면 데이터베이스에서 찾은 사용자 이름과 암호가 다시 패키징됩니다. 그런 다음 돌아옵니다.
여기에 사진 설명 삽입
계정 인증을 수행하면 사진에서 볼 수있는 정보 정보가 반환되며, 정보가 null이 아닌 경우 비밀번호를 확인합니다.
여기에 사진 설명 삽입
입력 후 먼저 CredentialsMatcher 객체를 얻었고,이 객체에 대해 인터페이스 구현 클래스를 찾고 비밀번호 매칭 메소드를 구현했음을 발견 했으므로 사용자 정의 객체를 호출합니다. 물론 사용자 정의하지 않으면 기본 암호화 암호가 인증을 비교하는 데 사용됩니다.
여기에 사진 설명 삽입
여기에 사진 설명 삽입
그런 다음 계속해서 암호 인증 단계로 들어갑니다.
여기에 사진 설명 삽입
익숙한 장소를 다시 입력하고 두 개의 비밀번호를 확인합니다. 암호를 확인하기 위해이 클래스의 메서드를 호출하는 것은 실제로 단일 문자 순회 비교입니다. 정확하지 않으면 false를 반환
여기에 사진 설명 삽입
하고 결국 종료됩니다. 꽃으로 완성 ~ 프로젝트 주소를 아래에 적어 주시면 필요한 친구가 받아드립니다.

코드 주소

코드 소스 코드 주소, 원하는 경우 선택 ~

추천

출처blog.csdn.net/qq_41762594/article/details/107361997