真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼

本人野生程序員一名,了解了一些微服務(wù)架構(gòu)、前后端分離、SPA的知識后就想試試做點(diǎn)什么東西。之前一直做后端,前端只是有基礎(chǔ)知識。之前學(xué)習(xí)過angularjs,但當(dāng)時就是一臉懵逼(完全看不懂是啥)就放棄了。最近又學(xué)了Vue,這次感覺總算明白了一些,但其中也跳過很多坑(應(yīng)該還會更多),在這里寫下來記錄一下吧。

創(chuàng)新互聯(lián)長期為千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為東海企業(yè)提供專業(yè)的成都做網(wǎng)站、成都網(wǎng)站設(shè)計、成都外貿(mào)網(wǎng)站建設(shè)東海網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。

說回主題,之前傳統(tǒng)登錄認(rèn)證的方法基本是由服務(wù)器端提供一個登錄頁面,頁面中的一個form輸入username和password后POST給服務(wù)器,服務(wù)器將這些信息與DB或Ldap中的用戶信息對比,成功則將這個用戶信息記錄到session中。

這里我就跳了第一個大坑。傳統(tǒng)方式前后端不分離,后端負(fù)責(zé)頁面渲染,但是在前后分離的情況下,后端只負(fù)責(zé)通過暴露的RestApi提供數(shù)據(jù),而頁面的渲染、路由都由前端完成。因為rest是無狀態(tài)的,因此也就不會有session記錄到服務(wù)器端。

之前一直使用SpringSecurity+Cas+Ldap來做SSO,但是使用Vue做前端后我怎都想不出用之前的方法做SSO(這個坑真的爬了好久才爬出來)。后來終于想明白了上面說的session的問題(我是這么認(rèn)為的也可能不對,CAS也有RestApi,但是按官網(wǎng)配置沒成功,放棄了)。

第一個問題,該如何解決SSO的問題呢,要說到JWT。JWT是個規(guī)范,各種語言有各種語言的實(shí)現(xiàn),可以去官網(wǎng)查到。我淺薄的理解是有一個認(rèn)證服務(wù)(你自己寫的,Db、Ldap什么都可以)這個認(rèn)證服務(wù)會通過用戶的提交信息判斷認(rèn)證是否成功,如果成功則查詢出一些用戶的信息(用戶名、角色什么的),然后JWT把這些信息加密成為一個token,返回給客戶端瀏覽器,瀏覽器把這些信息存儲在localstorage中,以后每次訪問資源都會在header中攜帶這個信息,服務(wù)器收到請求后使用和加密時相同的key解密密文,如果解密成功則視為用戶已經(jīng)認(rèn)證過(當(dāng)然你可以在加密時添加以一個過期時間)也就完成了SSO。使用解密出的角色信息你就可以判斷這個用戶是否有權(quán)限執(zhí)行一些業(yè)務(wù)。這樣做完后感覺好像SpringSecurity、Cas在SPA應(yīng)用中的SSO似乎沒什么作用了,目前我是這么認(rèn)為的(當(dāng)然可能不對)

第一個問題差不多解決了,來說第二個問題。之前因為有session的存在,在訪問受保護(hù)的資源時如果服務(wù)器端沒有當(dāng)前用戶的session,則會強(qiáng)制跳轉(zhuǎn)到登錄頁。那在前后分離的情況下要如何實(shí)現(xiàn)這個需求。思路是這樣的:利用Vue-Router的全局路由鉤子,在訪問任何頁面時先判斷l(xiāng)ocalStorage中是否存在JWT加密后的token并且token是否過期,如果存在且沒有過期則正常跳轉(zhuǎn)到請求的頁面,不存在或者過期則跳轉(zhuǎn)到登錄頁重新認(rèn)證。

思路說完了,上代碼

1.首先你需要一個Ldap,我使用的是AD。這里我建立了一個叫minibox.com的域,并且添加了一個Employees的OU,其中有2個子OU,子OU中創(chuàng)建了2個用戶。

Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼

在Groups中新建一些組,把之前創(chuàng)建的用戶加入到組中,這樣用戶就擁有了角色。

Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼

2.搭建SpringBoot環(huán)境

2.1pom文件


 4.0.0
 minibox
 an
 0.0.1-SNAPSHOT
  
  
    org.springframework.boot
    spring-boot-starter-parent
    1.5.1.RELEASE
  
  
  
    
    
      org.springframework.boot
      spring-boot-starter-web
    
    
     
      org.springframework.boot 
      spring-boot-starter-test 
      test 
     
    
    
      org.springframework.boot
      spring-boot-starter-hateoas
    
    
    
      org.springframework.boot
      spring-boot-devtools
      true
    
    
    
      io.jsonwebtoken
      jjwt
      0.7.0
    
    
    
      org.springframework.ldap
      spring-ldap-core
      2.3.1.RELEASE
    
    
    
      com.alibaba
      fastjson
      1.2.24
    
  
  
  
    
      
        org.springframework.boot
        spring-boot-maven-plugin
      
      
      
        org.springframework.boot
        spring-boot-maven-plugin
        
          
            org.springframework
            springloaded
            1.2.0.RELEASE
          
        
      
    
  

2.2應(yīng)用配置文件

#Logging_config
logging.level.root=INFO
logging.level.org.springframework.web=WARN
logging.file=minibox.log

#server_config
#使用了SSL,并且在ldap配置中使用了ldaps,這里同時也需要把AD的證書導(dǎo)入到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設(shè)置為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

3.Spring主配置類

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//相當(dāng)于@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);
  }
}

3.1提供認(rèn)證服務(wù)的類

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實(shí)例
   */
  private class EmployeeAttributesMapper implements AttributesMapper {
    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狀態(tài)碼
   */
  @CrossOrigin//因為需要跨域訪問,所以要加這個注解
  @RequestMapping(method = RequestMethod.POST)
  public ResponseEntity 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);
      //如果驗證成功根據(jù)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 userInfo = new HashMap();
      userInfo.put("token", compactJws);
      userInfo.put("displayName", employee.getDisplayName());
      userInfo.put("tokenExpired", tokenExpired.getTime());
      return new ResponseEntity(JSON.toJSONString(userInfo , SerializerFeature.DisableCircularReferenceDetect) , HttpStatus.OK);
    } catch (Exception e) {
      //登錄失敗,返回失敗HTTP狀態(tài)碼
      return new ResponseEntity(HttpStatus.UNAUTHORIZED);
    } finally {
      //關(guān)閉ldap連接
      LdapUtils.closeContext(ctx);
    }
  }

}

4.前端Vue

4.1使用Vue-cli搭建項目,并使用vue-router和vue-resource,不了解的可以搜索下

4.2 main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import store from './store/store'
import 'bootstrap/dist/css/bootstrap.css'
import App from './App'
import Login from './components/login'
import Hello from './components/hello'

Vue.use(VueRouter)
Vue.use(VueResource)
//Vue-resource默認(rèn)以payload方式提交數(shù)據(jù),這樣設(shè)置之后以formData方式提交
Vue.http.options.emulateJSON = true;

const routes = [
 {
  path: '/login',
  component : Login
 },{
  path: '/hello',
  component: Hello
 }
]

const router = new VueRouter({
 routes
})

//默認(rèn)導(dǎo)航到登錄頁
router.push('/login')

/*
全局路由鉤子
訪問資源時需要驗證localStorage中是否存在token
以及token是否過期
驗證成功可以繼續(xù)跳轉(zhuǎn)
失敗返回登錄頁重新登錄
 */
router.beforeEach((to, from, next) => {
 if(localStorage.token && new Date().getTime() < localStorage.tokenExpired){
  next()
 }
 else{
  next('/login')
 }
})

new Vue({
 el: '#app',
 template: '',
 components: { App },
 router,
 store
})

4.3 App.vue





4.4 login.vue





5效果

5.1訪問http://localhost:8000時被導(dǎo)航到登錄頁

Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼

5.2提交登錄信息并取得token,跳轉(zhuǎn)下一頁

Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼

到這里整個功能就完成了。本人也是菜鳥一枚,理解有錯誤的地方還請各位老師指正。打算把整個分布式系統(tǒng)的開發(fā)過程記錄下來。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。


網(wǎng)頁標(biāo)題:Vue+Jwt+SpringBoot+Ldap完成登錄認(rèn)證的示例代碼
文章鏈接:http://weahome.cn/article/ipoied.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部