>웹 프론트엔드 >JS 튜토리얼 >Vue+Jwt+SpringBoot+Ldap을 사용하여 로그인 인증을 완료하는 방법

Vue+Jwt+SpringBoot+Ldap을 사용하여 로그인 인증을 완료하는 방법

php中世界最好的语言
php中世界最好的语言원래의
2018-05-28 14:56:264266검색

이번에는 Vue+Jwt+SpringBoot+Ldap을 사용하여 로그인 인증을 완료하는 방법과 Vue+Jwt+SpringBoot+Ldap을 사용하여 로그인 인증을 완료할 때 주의사항이 무엇인지 보여드리겠습니다. 살펴보자.

기존의 기존 로그인 인증 방식은 기본적으로 서버 측에서 로그인 페이지를 제공했는데, 페이지의 양식에 사용자 이름과 비밀번호를 입력한 후 서버에서 이 정보를 DB 또는 Ldap의 사용자 정보와 비교했습니다. , 이 사용자 정보는 세션에 기록됩니다.

여기서 나는 첫 번째 큰 구덩이에 뛰어들었습니다. 기존 방식에서는 프런트엔드와 백엔드가 분리되지 않고 백엔드가 페이지 렌더링을 담당하지만, 프런트엔드와 백엔드가 분리되면 백엔드는 노출된 RestApi를 통한 데이터 제공만 담당하게 되며, 페이지의 렌더링 및 라우팅은 프런트 엔드에서 완료됩니다. REST는 상태 비저장이므로 서버에 기록된 세션이 없습니다.

이전에는 SpringSecurity+Cas+Ldap을 사용하여 SSO를 했는데, Vue를 프런트엔드로 사용한 후에는 이전 방법으로 SSO를 할 생각을 할 수 없었습니다(빠지는 데 시간이 오래 걸렸습니다). 이 구덩이). 나중에 위에서 언급한 세션 문제를 드디어 알아냈습니다(그렇다고 생각했는데 틀렸을 수도 있습니다. CAS에도 RestApi가 있는데 공식 홈페이지에 따라 구성이 안되서 포기했습니다).

첫 번째 질문은 SSO 문제를 해결하는 방법입니다. JWT에 대해 이야기해 보겠습니다. JWT는 스펙이며, 다양한 언어로 구현되어 있습니다. 공식 홈페이지에서 확인하실 수 있습니다. 내 피상적인 이해는 인증 서비스(직접 작성, Db, Ldap 등)가 있다는 것입니다. 이 인증 서비스는 사용자가 제출한 정보를 기반으로 인증 성공 여부를 판단합니다. 성공하면 일부 사용자 정보(사용자)를 쿼리합니다. 이름, 역할 등), JWT는 이 정보를 토큰으로 암호화하여 클라이언트 브라우저에 반환합니다. 브라우저는 나중에 리소스에 액세스할 때마다 이 정보를 헤더에 전달합니다. 서버는 요청을 받은 후 이를 암호화 시와 동일한 키로 복호화하는데 성공하면 사용자가 인증된 것으로 간주되며(물론 암호화 중에 만료 시간을 추가할 수 있음) 완료되었습니다. 해독된 역할 정보를 사용하면 이 사용자에게 일부 서비스를 수행할 수 있는 권한이 있는지 확인할 수 있습니다. 이렇게 하고 나면 SPA 애플리케이션에서 SSO에 SpringSecurity와 Cas가 아무런 역할도 하지 않는 것 같은 느낌이 듭니다. (물론 틀렸을 수도 있습니다)

첫 번째 문제는 거의 해결되었으니 두 번째 문제에 대해 이야기해 보겠습니다. . 이전에는 세션이 존재하기 때문에 보호된 리소스에 액세스할 때 서버에 현재 사용자의 세션이 없으면 강제로 로그인 페이지로 이동해야 했습니다. 전면과 후면이 분리된 경우 이 요구 사항을 달성하는 방법. 아이디어는 다음과 같습니다. Vue-Router의 전역 라우팅 후크를 사용하여 페이지에 액세스할 때 localStorage에 JWT 암호화 토큰이 있는지 먼저 확인하고 토큰이 만료되지 않은 경우 요청된 페이지로 이동합니다. 그렇지 않은 경우 존재하거나 만료된 경우 재인증을 위해 로그인 페이지로 이동합니다.

아이디어는 충분하고 코드로 넘어가겠습니다

1 먼저 LDAP가 필요합니다. 저는 AD를 사용합니다. 여기서는 minibox.com이라는 도메인을 만들고 2개의 하위 OU가 있는 Employees OU를 추가하고 하위 OU에 2명의 사용자를 만들었습니다.

그룹스에서 새 그룹을 만들고 이전에 만든 사용자를 그룹에 추가하여 사용자가 역할을 갖도록 합니다. build build build build bule reee

2.2 application

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>minibox</groupId>
 <artifactId>an</artifactId>
 <version>0.0.1-SNAPSHOT</version>
  <!-- Inherit defaults from Spring Boot -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
  </parent>
  <!-- Add typical dependencies for a web application -->
  <dependencies>
    <!-- MVC -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring boot test -->
    <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <scope>test</scope> 
    </dependency> 
    <!-- spring-boot-starter-hateoas -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>
    <!-- 热启动 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>
    <!-- JWT -->
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.7.0</version>
    </dependency>
    <!-- Spring Ldap -->
    <dependency>
      <groupId>org.springframework.ldap</groupId>
      <artifactId>spring-ldap-core</artifactId>
      <version>2.3.1.RELEASE</version>
    </dependency>
    <!-- fastjson -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.24</version>
    </dependency>
  </dependencies>
  <!-- Package as an executable jar -->
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <!-- Hot swapping -->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <dependencies>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.0.RELEASE</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>
3 Vue

4.1 Vue-cli를 사용하여 프로젝트를 빌드하고, 이해가 되지 않으면 vue-router 및 vue-resource를 사용하여 검색할 수 있습니다.

4.2 main.js

#Logging_config
logging.level.root=INFO
logging.level.org.springframework.web=WARN
logging.file=minibox.log
#server_config
#使用了SSL,并且在ldap配置中使用了ldaps,这里同时也需要把AD的证书导入到server.keystore中。具体的可以查看java的keytool工具
server.port=8443
server.ssl.key-store=classpath:server.keystore
server.ssl.key-store-password=minibox
server.ssl.key-password=minibox
#jwt
#jwt加解密时使用的key
jwt.key=minibox
#ldap_config
#ldap配置信息,注意这里的userDn一定要写这种形式。referral设置为follow,说不清用途,似乎只有连接AD时才需要配置
ldap.url=ldaps://192.168.227.128:636
ldap.base=ou=Employees,dc=minibox,dc=com
ldap.userDn=cn=Administrator,cn=Users,dc=minibox,dc=com
ldap.userPwd=qqq111!!!!
ldap.referral=follow
ldap.domainName=@minibox.com

4.3 App.vue

package an;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
@SpringBootApplication//相当于@Configuration,@EnableAutoConfiguration,@ComponentScan
public class Application {
  /*
  * SpringLdap配置。通过@Value注解读取之前配置文件中的值
  */
  @Value("${ldap.url}")
  private String ldapUrl;
  @Value("${ldap.base}")
  private String ldapBase;
  @Value("${ldap.userDn}")
  private String ldapUserDn;
  @Value("${ldap.userPwd}")
  private String ldapUserPwd;
  @Value("${ldap.referral}")
  private String ldapReferral;
  /*
  *SpringLdap的javaConfig注入方式
  */
  @Bean
  public LdapTemplate ldapTemplate() {
    return new LdapTemplate(contextSourceTarget());
  }
  @Bean
  public LdapContextSource contextSourceTarget() {
    LdapContextSource ldapContextSource = new LdapContextSource();
    ldapContextSource.setUrl(ldapUrl);
    ldapContextSource.setBase(ldapBase);
    ldapContextSource.setUserDn(ldapUserDn);
    ldapContextSource.setPassword(ldapUserPwd);
    ldapContextSource.setReferral(ldapReferral);
    return ldapContextSource;
  }
  public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
  }
}
4.4 login.vue

package an.auth;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import an.entity.Employee;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@RestController
@RequestMapping("/auth")
public class JwtAuth {
  //jwt加密密匙
  @Value("${jwt.key}")
  private String jwtKey;
  //域名后缀
  @Value("${ldap.domainName}")
  private String ldapDomainName;
  //ldap模板
  @Autowired
  private LdapTemplate ldapTemplate;
  /**
   * 将域用户属性通过EmployeeAttributesMapper填充到Employee类中,返回一个填充信息的Employee实例
   */
  private class EmployeeAttributesMapper implements AttributesMapper<Employee> {
    public Employee mapFromAttributes(Attributes attrs) throws NamingException, javax.naming.NamingException {
      Employee employee = new Employee();
      employee.setName((String) attrs.get("sAMAccountName").get());
      employee.setDisplayName((String) attrs.get("displayName").get());
      employee.setRole((String) attrs.get("memberOf").toString());
      return employee;
    }
  }
  /**
   * @param username 用户提交的名称
   * @param password 用户提交的密码
   * @return 成功返回加密后的token信息,失败返回错误HTTP状态码
   */
  @CrossOrigin//因为需要跨域访问,所以要加这个注解
  @RequestMapping(method = RequestMethod.POST)
  public ResponseEntity<String> authByAd(
      @RequestParam(value = "username") String username,
      @RequestParam(value = "password") String password) {
    //这里注意用户名加域名后缀 userDn格式:anwx@minibox.com
    String userDn = username + ldapDomainName;
    //token过期时间 4小时
    Date tokenExpired = new Date(new Date().getTime() + 60*60*4*1000);
    DirContext ctx = null;
    try {
      //使用用户名、密码验证域用户
      ctx = ldapTemplate.getContextSource().getContext(userDn, password);
      //如果验证成功根据sAMAccountName属性查询用户名和用户所属的组
      Employee employee = ldapTemplate                            .search(query().where("objectclass").is("person").and("sAMAccountName").is(username),
              new EmployeeAttributesMapper())
          .get(0);
      //使用Jwt加密用户名和用户所属组信息
      String compactJws = Jwts.builder()
          .setSubject(employee.getName())
          .setAudience(employee.getRole())
          .setExpiration(tokenExpired)
          .signWith(SignatureAlgorithm.HS512, jwtKey).compact();
      //登录成功,返回客户端token信息。这里只加密了用户名和用户角色,而displayName和tokenExpired没有加密
      Map<String, Object> userInfo = new HashMap<String, Object>();
      userInfo.put("token", compactJws);
      userInfo.put("displayName", employee.getDisplayName());
      userInfo.put("tokenExpired", tokenExpired.getTime());
      return new ResponseEntity<String>(JSON.toJSONString(userInfo , SerializerFeature.DisableCircularReferenceDetect) , HttpStatus.OK);
    } catch (Exception e) {
      //登录失败,返回失败HTTP状态码
      return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
    } finally {
      //关闭ldap连接
      LdapUtils.closeContext(ctx);
    }
  }
}

5 효과

5.1 http://localhost:8000 접속 시 로그인 페이지로 이동됩니다

5.2 로그인 정보를 입력하고 토큰을 획득하면 다음 페이지로 이동합니다.

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 믿습니다. 더 흥미로운 내용을 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 도서:

Vue를 사용하여 AdminLTE 템플릿을 통합하는 방법

JS로 tic-tac-toe 게임을 만드는 방법

위 내용은 Vue+Jwt+SpringBoot+Ldap을 사용하여 로그인 인증을 완료하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.