現在國內前后端很多公司都在使用前后端分離的開發方式,雖然也有很多人並不贊同前后端分離,比如以下這篇博客就很有意思:
https://www.aliyun.com/jiaocheng/650661.html
我們從技術角度來看的話:
http://blog.jobbole.com/111624/
http://www.360doc.com/content/18/0511/06/36490684_752894279.shtml
摘要:
為什么選擇前后端分離
- 在以前傳統的網站開發中,前端一般扮演的只是切圖的工作,只是簡單地將UI設計師提供的原型圖實現成靜態的HTML頁面,而具體的頁面交互邏輯,比如與后台的數據交互工作等,可能都是由后台的開發人員來實現的,或者是前端是緊緊的耦合后台。比如,以前淘寶的Web基本上都是基於MVC框架webx,架構決定了前端只能依賴后端。所以他們的開發模式依然是,前端寫好靜態demo,后端翻譯成VM模版,這種模式的問題就不說了,被吐槽了很久。
- 而且更有可能后台人員直接兼顧前端的工作,一邊實現API接口,一邊開發頁面,兩者互相切換着做,而且根據不同的url動態拼接頁面,這也導致后台的開發壓力大大增加。前后端工作分配不均。不僅僅開發效率慢,而且代碼難以維護。而前后端分離的話,則可以很好的解決前后端分工不均的問題,將更多的交互邏輯分配給前端來處理,而后端則可以專注於其本職工作,比如提供API接口,進行權限控制以及進行運算工作。而前端開發人員則可以利用nodejs來搭建自己的本地服務器,直接在本地開發,然后通過一些插件來將api請求轉發到后台,這樣就可以完全模擬線上的場景,並且與后台解耦。前端可以獨立完成與用戶交互的整一個過程,兩者都可以同時開工,不互相依賴,開發效率更快,而且分工比較均衡。
當然,站在我的角度,CTO要求我們用前后端分離我就用好了,前后端分離的權限控制問題要稍微復雜一些,我們最近的項目采用security+jwt的方式來實現前后端分離的權限控制。
Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,為應用系統提供聲明式的安全訪問控制功能,減少了為企業系統安全控制編寫大量重復代碼的工作。
關於security的其他知識可以看:
https://www.cnblogs.com/jiangwz/p/9133160.html
Json web token (JWT),是為了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標准((RFC 7519)。該標准被設計為緊湊且安全的,一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息。當然該標准也可直接被用於認證,也可被加密。
關於JWT可以看下面這篇博客:
https://www.cnblogs.com/jiangwz/p/9503914.html
spring security + jwt的具體實現思路是:
用戶登陸后台,后台生成一個jwt簽名返回給前端,前端每次請求將簽名放在Header,后台驗證簽名是否正確。
關於security的部分這里不再贅述
jwt
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
驗證token
public class JwtAuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter { @Autowired private CustomerUserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; private String tokenHeader="Authorization"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResopnse = (HttpServletResponse) response; if("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) { httpResopnse.setStatus(HttpServletResponse.SC_OK); } else { String authToken = httpRequest.getHeader(this.tokenHeader); if(authToken!=null) authToken =authToken.substring(7); String username = jwtTokenUtil.getUsernameFromToken(authToken); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(authToken, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); } } }
JWT工具類中,根據USER信息生成token
public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put(CLAIM_KEY_CREATED, new Date()); ...... return generateToken(claims); }
然后前端需要在每次的請求中將這個token放入請求頭中。