<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      一文助您成為Java.Net雙平臺高手

      寫在前面:本文乃標(biāo)題黨,不是月經(jīng)貼,側(cè)重于Web開發(fā)差異,或細(xì)節(jié)或概述,若有不對之處,還請各位讀者本著友好互助的心態(tài)批評指正。由于博客園中.Neter較多(個人感覺),因此本文也可以作為.Neter到Java開發(fā)的快速入門。

      恕本文的不嚴(yán)謹(jǐn),評論里有說到.net core的,其實(shí)可看作是另一個平臺。雖然.net core目前社區(qū)討論較多,但畢竟出生不久(相對來說),市場體量應(yīng)該還遠(yuǎn)未達(dá)到傳統(tǒng).NET。所以本文仍基于傳統(tǒng).NET描述,但部分文字也適用于.net core。另鄙人對微軟的開源策略亦持樂觀態(tài)度,在總述中稍有提及。


      總述

      在.Net開發(fā)中,微軟官方框架類可以很好的解決的大部分問題,開發(fā)人員可以心安理得的在一畝三分地騰挪躲閃出花來;偶有一些優(yōu)(zhao)秀(chao)的開源庫,各庫的關(guān)注點(diǎn)也基本不會重樣;所以.Neter只要按部就班即可。而Java喜歡定義各種規(guī)范,各路大神各自實(shí)現(xiàn),因此一個概念常常會有很多的第三方庫,雖然有Spring這種殺手級框架,不過基于IOC和AOP的設(shè)定,Spring家族也變得異常龐大,在編碼時需要引入大量的annotation來織入邏輯;雖然貌似最大程度的解耦了各組件,但導(dǎo)致代碼的可讀性和可調(diào)試性非常不好,碎片化非常嚴(yán)重。不過也因?yàn)槿绱耍琂ava社區(qū)成為設(shè)計思想的孕育地,并常常出現(xiàn)一些讓人擊節(jié)的設(shè)計模式。其中的概念傳播到隔壁.Net圈,圈內(nèi)小白往往一臉懵逼,而少數(shù)大佬不管不顧拿來套用,往往是用錯了,或者讓人不知所以。

      籠統(tǒng)來說,.Net框架隱藏細(xì)節(jié),簡便清晰,套路單一,但常陷入知其然不知其所以然的懵逼境地;Java&Spring注解隱藏細(xì)節(jié),概念繁多,沒有方向感或有被繞暈的風(fēng)險,但一旦破位而出,則縱橫捭闔天地之大可任意施展至其它平臺。不過兩者差異隨著.Net的開源以肉眼不可見的速度緩慢消失,特別是最近幾年,.Net在語法層面已經(jīng)超越了Java良多,Java雖然一時半會抹不開面子,但也一直在改進(jìn)。到的本文撰寫時分,借用不知名網(wǎng)友語:“C#語法已經(jīng)達(dá)到Java20,用戶量撐死Java7,生態(tài)Java1.4”。

      兩者競爭主要集中在Web開發(fā)領(lǐng)域。目前在該領(lǐng)域,Spring Boot已基本成為事實(shí)上Java平臺的“官方框架”,我想大部分開發(fā)人員并不會在意背后的實(shí)現(xiàn)細(xì)節(jié),從這個方面來講,兩個平臺的開發(fā)模式有一定程度的相似。


      數(shù)據(jù)持久層

      為啥這節(jié)標(biāo)題不是ORM呢?畢竟ORM現(xiàn)在是業(yè)界標(biāo)準(zhǔn),很難想象這個時代還需要手寫SQL,還需要手動操作JDBC/ADO;如果你打算這么干,一定會被年輕一輩打心眼里鄙視:)

      Java

      ORM:十多年前,Hibernate就開始興起,它提供了半對象化的HQL和完全的面向?qū)ο驫BC。之后也出現(xiàn)了其它一些ORM比如TopLink。

      JPA:JDK5引入,是SUN公司為了統(tǒng)一目前眾多ORM而提出的ORM規(guī)范(又犯了定義規(guī)范的癮)。這個規(guī)范出來后,很多ORM表示支持,但以前的還得維護(hù)啊,所以像Hibernate就另外建了一個分支叫Hibernate JPA。網(wǎng)友benjaminlee1所言:“JPA的出現(xiàn)只是用于規(guī)范現(xiàn)有的ORM技術(shù),它不能取代現(xiàn)有的Hibernate等ORM框架,相反,采用JPA開發(fā)時,我們?nèi)詫⑹褂眠@些ORM框架,只是此時開發(fā)出來的應(yīng)用不在依賴于某個持久化提供商。應(yīng)用可以在不修改代碼的情況下載任何JPA環(huán)境下運(yùn)行,真正做到低耦合,可擴(kuò)展的程序設(shè)計。類似于JDBC,在JDBC出現(xiàn)以前,我們的程序針對特性的數(shù)據(jù)庫API進(jìn)行編程,但是現(xiàn)在我們只需要針對JDBC API編程,這樣能夠在不改變代碼的情況下就能換成其他的數(shù)據(jù)庫。”

      Spring Data JPA:有了JPA,我們就可以不在意使用哪個ORM了,但是Spring Data JPA更進(jìn)一步(為Spring家族添磚加瓦),按約定的方式自動給我們生成持久化代碼,當(dāng)然它底層還是要依賴各路ORM的。相關(guān)資料:使用 Spring Data JPA 簡化 JPA 開發(fā)

      Mybatis:隨著時間的流逝,Hibernate曾經(jīng)帶來的榮耀已經(jīng)被臃腫丑陋的配置文件,無法優(yōu)化的查詢語句淹沒。很多人開始懷念可一手掌控數(shù)據(jù)操作的時代,于是Mybatis出現(xiàn)了。Mybatis不是一個完整的ORM,它只完成了數(shù)據(jù)庫返回結(jié)果到對象的映射,而存取邏輯仍為SQL,寫在Mapper文件中,它提供的語法在一定程度上簡化了SQL的編寫,最后Mybatis將SQL邏輯映射到接口方法上(在Mapper文件中指定<mapper namespace="xxx">,其中xxx為映射的DAO接口)。針對每個表寫通用增刪改查的Mapper SQL既枯燥又易出錯,所以出現(xiàn)了Mybatis-Generator之類的代碼生成工具,它能基于數(shù)據(jù)表生成實(shí)體類、基本CRUD的Mapper文件、對應(yīng)的DAOInterface。

      Mybatis-Plus:在Mybatis的基礎(chǔ)上,提供了諸如分頁、復(fù)雜條件查詢等功能,基礎(chǔ)CRUD操作不需要額外寫SQL Mapper了,只要DAO接口繼承BaseMapper接口即可。當(dāng)然為了方便,它也提供了自己的代碼生成器。

      .NET

        ORM:主流Entity Framework,除開ORM功能外,它還提供了Code first、DB first、T4代碼生成等特性。性能上與Hibernate一個等級,但使用便捷性和功能全面性較好,更別說還有l(wèi)inq的加持。


      認(rèn)證&授權(quán)&鑒權(quán)

      認(rèn)證是檢測用戶/請求是否合法,授權(quán)是賦予合法用戶相應(yīng)權(quán)限,鑒權(quán)是鑒別用戶是否有請求某項(xiàng)資源的權(quán)限(認(rèn)證和授權(quán)一般是同時完成)。我們以web為例。

      C#/Asp.net mvc

      提供了兩個Filter:IAuthenticationFilter 和 AuthorizeAttribute,前者用于認(rèn)證授權(quán),后者用于鑒權(quán)。

       1 //IAuthenticationFilter 認(rèn)證,認(rèn)證是否合法用戶
       2 public class AdminAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
       3 {
       4     public void OnAuthentication(AuthenticationContext filterContext)
       5     {
       6         IPrincipal user = filterContext.Principal;
       7         if (user == null || !user.Identity.IsAuthenticated)
       8         {
       9             HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
      10             if (authCookie != null)
      11             {
      12                 FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
      13                 if (ticket != null && !string.IsNullOrEmpty(ticket.UserData))
      14                 {
      15                     var userId = Convert.ToInt32(ticket.UserData);
      16                     user = EngineContext.Resolve<PFManagerService>().GetManager(userId);
      17                     filterContext.Principal = user; //后續(xù)會傳遞給HttpContext.Current.User
      18                 }
      19             }
      20         }
      21     }
      22 
      23     public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
      24     {
      25         // 認(rèn)證失敗執(zhí)行
      26     }
      27 }
      View Code

      認(rèn)證成功后,將user賦給filterContext.Principal(第17行),filterContext.Principal接收一個IPrincipal接口對象,該接口有個 bool IsInRole(string role) 方法,用于后續(xù)的鑒權(quán)過程。

       1 public class AdminAuthorizationFilter : AuthorizeAttribute
       2 {        
       3     public override void OnAuthorization(AuthorizationContext filterContext)
       4     {
       5         //childaction不用授權(quán)
       6         if (filterContext.IsChildAction)
       7             return;
       8 
       9         if (!filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) && !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
      10         {           
      11             if (filterContext.HttpContext.User != null && filterContext.HttpContext.User.Identity.IsAuthenticated)
      12             {
      13                 var controllerName = filterContext.RouteData.Values["controller"].ToString().ToLower();
      14                 var actionName = filterContext.RouteData.Values["action"].ToString().ToLower();
      15                 //只要登錄,則都能訪問工作臺
      16                 if (controllerName.ToLower() == "home" && actionName.ToLower() == "index")
      17                     this.Roles = string.Empty;
      18                 else
      19                 {
      20                     var roleIds = EngineContext.Resolve<BEModuleService>().GetRoleIdsHasModuleAuthorization(controllerName, actionName, MasonPlatformType.AdminPlatform);
      21                     if (roleIds == null)
      22                     {
      23                         filterContext.Result = new HttpNotFoundResult();
      24                         return;
      25                     }
      26                     //將資源所需權(quán)限賦給成員變量Roles
      27                     this.Roles = string.Join(",", roleIds);
      28                 }
      29             }
      30         }
      31 
      32         base.OnAuthorization(filterContext);
      33     }
      34 }
      View Code

      注意第27行,我們將擁有該資源的所有權(quán)限賦給Roles,之后AuthorizeAttribute會循環(huán)Roles,依次調(diào)用當(dāng)前用戶(上述的filterContext.Principal)的IsInRole方法,若其中一個返回true則表明用戶有訪問當(dāng)前資源的權(quán)限。

      Java/Spring Security

      也提供了兩個類,一個Filter和一個Interceptor:AuthenticationProcessingFilter用于用戶認(rèn)證授權(quán),AbstractSecurityInterceptor用于鑒權(quán)。Spring Security基于它們又封裝了幾個類,主要幾個:WebSecurityConfigurerAdapter、FilterInvocationSecurityMetadataSource、AccessDecisionManager、UserDetailsService。另外還有各類注解如@EnableGlobalMethodSecurity等。(以下代碼含有一點(diǎn)jwt邏輯)

      WebSecurityConfigurerAdapter:

       1 @Configuration
       2 @EnableWebSecurity
       3 @EnableGlobalMethodSecurity(prePostEnabled = true)
       4 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
       5     @Autowired
       6     private JwtAuthenticationEntryPoint unauthorizedHandler;
       7 
       8     @Autowired
       9     private UserDetailsService userDetailsService;
      10 
      11     @Autowired
      12     private CustomPostProcessor postProcessor;
      13 
      14     @Autowired
      15     public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
      16         authenticationManagerBuilder
      17                 .userDetailsService(this.userDetailsService)
      18                 .passwordEncoder(passwordEncoder());
      19     }
      20 
      21     @Bean
      22     public PasswordEncoder passwordEncoder() {
      23         return new BCryptPasswordEncoder();
      24     }
      25 
      26     @Bean
      27     public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
      28         return new JwtAuthenticationTokenFilter();
      29     }
      30 
      31     @Override
      32     protected void configure(HttpSecurity httpSecurity) throws Exception {
      33         httpSecurity
      34                 // we don't need CSRF because our token is invulnerable
      35                 .csrf().disable()
      36                 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
      37                 // don't create session
      38                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
      39                 .authorizeRequests()
      40                 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
      41                 .anyRequest().authenticated().withObjectPostProcessor(postProcessor);
      42 
      43         // Custom JWT based security filter
      44         httpSecurity
      45                 .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
      46     }
      47 }
      View Code

      主要關(guān)注兩個方法configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder)和configure(HttpSecurity httpSecurity)。configureAuthentication主要用于設(shè)置UserDetailsService,加載用戶數(shù)據(jù)需要用到;configure用于設(shè)置資源的安全級別以及全局安全策略等。第41行withObjectPostProcessor,用于設(shè)置FilterInvocationSecurityMetadataSource和AccessDecisionManager,它們兩個用于鑒權(quán),下面會講到。

       1 @Component
       2 public class CustomPostProcessor implements ObjectPostProcessor<FilterSecurityInterceptor> {
       3     @Autowired
       4     private CustomFilterSecurityMetadataSource customFilterSecurityMetadataSource;
       5 
       6     @Autowired
       7     private CustomAccessDecisionManager customAccessDecisionManager;
       8 
       9     @Override
      10     public <T extends FilterSecurityInterceptor> T postProcess(T fsi) {
      11         fsi.setSecurityMetadataSource(customFilterSecurityMetadataSource); //1.路徑(資源)攔截處理
      12         fsi.setAccessDecisionManager(customAccessDecisionManager); //2.權(quán)限決策處理類
      13         return fsi;
      14     }
      15 }
      View Code

      UserDetailService(此處從數(shù)據(jù)庫獲取):

       1 @Service
       2 public class JwtUserDetailsServiceImpl implements UserDetailsService {
       3 
       4     @Autowired
       5     private UserRepository userRepository;
       6 
       7     @Override
       8     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       9         User user = userRepository.findByUsername(username);
      10 
      11         if (user == null) {
      12             throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
      13         } else {
      14             return JwtUserFactory.create(user);
      15         }
      16     }
      17 }
      View Code

      注意loadUserByUsername需要的參數(shù)名username是約定好的,在UsernamePasswordAuthenticationFilter中定義,value是從HttpServletRequest中獲取。

      FilterInvocationSecurityMetadataSource(用于獲取當(dāng)前請求資源所需的權(quán)限):

       1 /**
       2  * 路徑攔截處理類
       3  * <p>
       4  * 如果路徑屬于允許訪問列表,則不做攔截,放開訪問;
       5  * <p>
       6  * 否則,獲得路徑訪問所需角色,并返回;如果沒有找到該路徑所需角色,則拒絕訪問。
       7  */
       8 @Component
       9 public class CustomFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
      10     @Autowired
      11     private ApiRepository apiRepository;
      12 
      13     @Override
      14     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
      15         FilterInvocation fi = (FilterInvocation) object; //當(dāng)前請求對象
      16 
      17         List<ConfigAttribute> configAttributes = getMatcherConfigAttribute(fi.getRequestUrl(), fi.getRequest().getMethod()); // 獲得訪問當(dāng)前路徑所需要的角色
      18 
      19         return configAttributes.size() > 0 ? configAttributes : deniedRequest(); //返回當(dāng)前路徑所需角色,如果路徑?jīng)]有對應(yīng)角色,則拒絕訪問
      20     }
      21 
      22     @Override
      23     public Collection<ConfigAttribute> getAllConfigAttributes() {
      24         return null;
      25     }
      26 
      27     @Override
      28     public boolean supports(Class<?> clazz) {
      29         return FilterInvocation.class.isAssignableFrom(clazz);
      30     }
      31 
      32     /**
      33      * 獲取當(dāng)前路徑以及請求方式獲得所需要的角色
      34      *
      35      * @param url 當(dāng)前路徑
      36      * @return 所需角色集合
      37      */
      38     private List<ConfigAttribute> getMatcherConfigAttribute(String url, String method) {
      39         Set<Authority> authorities = new HashSet<>();
      40         // 1.根據(jù)url的開頭去數(shù)據(jù)庫模糊查詢相應(yīng)的api
      41 
      42         String prefix = url.substring(0, url.lastIndexOf("/"));
      43 
      44         prefix = StringUtil.isEmpty(prefix) ? url : prefix + "%";
      45 
      46         List<Api> apis = apiRepository.findByUriLikeAndMethod(prefix, method);
      47 
      48         // 2.查找完全匹配的api,如果沒有,比對pathMatcher是否有匹配的結(jié)果
      49         apis.forEach(api -> {
      50             String pattern = api.getUri();
      51 
      52             if (new AntPathMatcher().match(pattern, url)) {
      53                 List<Resource> resources = api.getResources();
      54 
      55                 resources.forEach(resource -> {
      56                     authorities.addAll(resource.getAuthorities());
      57                 });
      58             }
      59         });
      60 
      61         return authorities.stream().map(authority -> new SecurityConfig(authority.getId().toString())).collect(Collectors.toList());
      62     }
      63 
      64     /**
      65      * @return 默認(rèn)拒絕訪問配置
      66      */
      67     private List<ConfigAttribute> deniedRequest() {
      68         return Collections.singletonList(new SecurityConfig("ROLE_DENIED"));
      69     }
      70 }
      View Code

      AccessDecisionManager:

       1 /**
       2  * 權(quán)限決策處理類
       3  *
       4  * 判斷用戶的角色,如果為空,則拒絕訪問;
       5  *
       6  * 判斷用戶所有的角色中是否有一個包含在 訪問路徑允許的角色集合中;
       7  *
       8  * 如果有,則放開;否則拒絕訪問;
       9  */
      10 @Component
      11 public class CustomAccessDecisionManager implements AccessDecisionManager {
      12     @Override
      13     public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
      14         if (authentication == null) {
      15             throw new AccessDeniedException("permission denied");
      16         }
      17 
      18         //當(dāng)前用戶擁有的角色集合
      19         List<String> roleCodes = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
      20 
      21         //訪問路徑所需要的角色集合
      22         List<String> configRoleCodes = configAttributes.stream().map(ConfigAttribute::getAttribute).collect(Collectors.toList());
      23         for (String roleCode : roleCodes) {
      24             if (configRoleCodes.contains(roleCode)) {
      25                 return;
      26             }
      27         }
      28 
      29         throw new AccessDeniedException("permission denied");
      30     }
      31 
      32     @Override
      33     public boolean supports(ConfigAttribute attribute) {
      34         return true;
      35     }
      36 
      37     @Override
      38     public boolean supports(Class<?> clazz) {
      39         return true;
      40     }
      41 }
      View Code

      上述第19行和第22行分別為UserDetailService處取到的用戶擁有的權(quán)限和FilterInvocationSecurityMetadataSource取到的訪問資源需要的權(quán)限,兩者對比后即得出用戶是否有訪問該資源的權(quán)限。具體來說,鑒權(quán)的整個流程是:訪問資源時,會通過AbstractSecurityInterceptor攔截器攔截,其中會調(diào)用FilterInvocationSecurityMetadataSource的方法來獲取被攔截url所需的全部權(quán)限,再調(diào)用授權(quán)管理器AccessDecisionManager,這個授權(quán)管理器會通過spring的全局緩存SecurityContextHolder獲取用戶的權(quán)限信息,還會獲取被攔截的url和被攔截url所需的全部權(quán)限,然后根據(jù)所配的策略(有:一票決定,一票否定,少數(shù)服從多數(shù)等),如果權(quán)限足夠,則返回,權(quán)限不夠則報錯并調(diào)用權(quán)限不足頁面。

      題外話,登錄認(rèn)證可以認(rèn)為并非認(rèn)證授權(quán)的一部分,而是將身份令牌頒發(fā)給客戶端的過程,之后客戶端拿著身份令牌過來請求資源的時候才進(jìn)入上面的認(rèn)證授權(quán)環(huán)節(jié)。不過Spring Secuity中涉及到的認(rèn)證方法可以簡化登錄認(rèn)證的代碼編寫:

      1 final Authentication authentication = authenticationManager.authenticate(
      2         new UsernamePasswordAuthenticationToken(username, password)
      3 );
      4 
      5 SecurityContextHolder.getContext().setAuthentication(authentication);

      其中authenticationManager由框架提供,框架會根據(jù)上面說到的configureAuthentication提供合適的AuthenticationManager實(shí)例,認(rèn)證失敗時拋出異常,否則返回Authenticatio令牌并為用戶相關(guān)的SecurityContext設(shè)置令牌。需要注意的是,SecurityContext是存放在ThreadLocal中的,而且在每次權(quán)限鑒定的時候都是從ThreadLocal中獲取SecurityContext中對應(yīng)的Authentication所擁有的權(quán)限,并且不同的request是不同的線程,為什么每次都可以從ThreadLocal中獲取到當(dāng)前用戶對應(yīng)的SecurityContext呢?在Web應(yīng)用中這是通過SecurityContextPersistentFilter實(shí)現(xiàn)的,默認(rèn)情況下其會在每次請求開始的時候從session中獲取SecurityContext,然后把它設(shè)置給SecurityContextHolder,在請求結(jié)束后又會將該SecurityContext保存在session中,并且在SecurityContextHolder中清除。當(dāng)用戶第一次訪問系統(tǒng)的時候,該用戶沒有SecurityContext,待登錄成功后,之后的每次請求就可以從session中獲取到該SecurityContext并把它賦予給SecurityContextHolder了,由于SecurityContextHolder已經(jīng)持有認(rèn)證過的Authentication對象了,所以下次訪問的時候也就不再需要進(jìn)行登錄認(rèn)證了。

      而上文說到的jwt,卻是cookie/session一生黑。它的機(jī)制是http請求頭部的令牌認(rèn)證。我們可以借助它在session過期后也能正常的認(rèn)證授權(quán),而不需要用戶重新登錄。

       1 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
       2 
       3     private final Log logger = LogFactory.getLog(this.getClass());
       4 
       5     @Autowired
       6     private UserDetailsService userDetailsService;
       7 
       8     @Autowired
       9     private JwtTokenUtil jwtTokenUtil;
      10 
      11     @Value("${jwt.header}")
      12     private String tokenHeader;
      13 
      14     @Override
      15     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
      16         final String requestHeader = request.getHeader(this.tokenHeader);
      17 
      18         String username = null;
      19         String authToken = null;
      20         if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
      21             authToken = requestHeader.substring(7);
      22             try {
      23                 username = jwtTokenUtil.getUsernameFromToken(authToken);
      24             } catch (IllegalArgumentException e) {
      25                 logger.error("an error occured during getting username from token", e);
      26             } catch (Exception e1) {
      27                 logger.error(e1.getMessage());
      28             }
      29         }
      30 
      31         if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
      32 
      33             // It is not compelling necessary to load the use details from the database. You could also store the information
      34             // in the token and read it from it. It's up to you ;)
      35             UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
      36 
      37             // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
      38             // the database compellingly. Again it's up to you ;)
      39             if (jwtTokenUtil.validateToken(authToken, userDetails)) {
      40                 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
      41                 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
      42                 logger.info("authenticated user " + username + ", setting security context");
      43                 SecurityContextHolder.getContext().setAuthentication(authentication);
      44             }
      45         }
      46 
      47         chain.doFilter(request, response);
      48     }
      49 }
      View Code

      當(dāng)然也可以不借助Spring Security,單純的實(shí)現(xiàn)jwt,那樣就需要自己實(shí)現(xiàn)認(rèn)證和授權(quán)過程了。

      在Spring Boot 1.5中,我們可以依靠重寫WebMvcConfigurerAdapter的方法來添加自定義攔截器,消息轉(zhuǎn)換器等;Spring Boot 2.0 后,該類被標(biāo)記為@Deprecated。方式改為實(shí)現(xiàn)WebMvcConfigurer接口。在Java中,攔截器(Interceptor)和Filter有所不同,前者更貼近AOP概念,而后者只有前置執(zhí)行。

      對比:Asp.net mvc相對清晰,可控性高;Spring Security隱藏了邏輯順序,涉及類較多,關(guān)鍵步驟散落各處,層級不清,容易讓新手困惑。還有其它的Java認(rèn)證框架如Shiro,也很流行,此處按過不表。


      非阻塞編程

      在web開發(fā)領(lǐng)域,傳統(tǒng)的實(shí)現(xiàn)異步的方式都比較復(fù)雜,比如 Java 中的 NIO,需要了解 channel,selector,buffer 這些概念,或者使用 netty 這樣的網(wǎng)絡(luò)框架。c/c++ 進(jìn)行異步/非阻塞編程,則需要理解 select,poll,epoll 等概念,開發(fā)與維護(hù)門檻較高。而且這部分的開發(fā)與業(yè)務(wù)無關(guān),那么封裝底層機(jī)制,推出一套開發(fā)框架的必要性就顯而易見了。概念上,.Net習(xí)慣稱為異步編程(Asynchronous programming),Java稱之為響應(yīng)式編程(Reactive Programming)。

      .Net/Asynchronous programming

      .Net4.5(C#5.0,2012年)開始,引入async/await關(guān)鍵字,在語法層面上將異步編程變得如同同步處理般清晰流暢,并在短時內(nèi)即推出了支持主流數(shù)據(jù)庫的異步組件。從接收請求到數(shù)據(jù)操作,開發(fā)人員能很方便的將傳統(tǒng)的同步代碼遷移為異步模式。之后幾年,如Python(3.5)、Nodejs(7.6)等紛紛效仿,成為事實(shí)上的語法標(biāo)準(zhǔn)。

      Java/Reactive Programming

      我們得先從Stream說起,Stream本身和響應(yīng)式編程沒關(guān)系,但之后的Reactive Streams在某種程度上繼承了它的某些概念。Java 8 引入了Stream,方便集合的聚合操作,它也支持lambda表達(dá)式作為操作參數(shù),可以將其看做Iterator。類似的語法在C#中也有,只是C#提供的是無侵入方式,集合本身就支持,更不用說Stream這個概念多么讓人混淆。相關(guān)資料:Java 8 中的 Streams API 詳解

      Stream的映射操作有map和flatmap,類似C#中Select和SelectMany的區(qū)別。

      Reactive Streams

      歷程

      響應(yīng)式流從2013年開始,作為提供非阻塞背壓的異步流處理標(biāo)準(zhǔn)的倡議。

      在2015年,出版了一個用于處理響應(yīng)式流的規(guī)范和Java API。 Java API 中的響應(yīng)式流由四個接口組成:Publisher<T>,Subscriber<T>,Subscription和Processor<T,R>。

      JDK 9在java.util.concurrent包中提供了與響應(yīng)式流兼容的API,它在java.base模塊中。 API由兩個類組成:Flow和SubmissionPublisher<T>。Flow類封裝了響應(yīng)式流Java API。 由響應(yīng)式流Java API指定的四個接口作為嵌套靜態(tài)接口包含在Flow類中:Flow.Processor<T,R>,F(xiàn)low.Publisher<T>,F(xiàn)low.Subscriber<T>和Flow.Subscription。

      Reactor是Reactive Streams的一個實(shí)現(xiàn)庫。鄙人認(rèn)為,Reactive Streams針對的場景是無邊界數(shù)據(jù)的enumerate處理,無邊界即數(shù)據(jù)/需求會被不停的生產(chǎn)出來,無法在事前確立循環(huán)規(guī)則(如循環(huán)次數(shù));另一方面,它又提供了單次處理的處理規(guī)則(如每次處理多少條數(shù)據(jù)/需求)。相關(guān)資料:聊聊reactive streams的backpressure

      Spring5.0開始提供響應(yīng)式 Web 編程支持,框架為Spring WebFlux,區(qū)別于傳統(tǒng)的Spring MVC同步模式。Spring WebFlux基于Reactor,其語法類似JS的Promise,并有一些靈活有用的特性如延時處理返回。具體用法可參看:(5)Spring WebFlux快速上手——響應(yīng)式Spring的道法術(shù)器 。就文中所說,目前(本文書寫時間)Spring Data對MongoDB、Redis、Apache Cassandra和CouchDB數(shù)據(jù)庫提供了響應(yīng)式數(shù)據(jù)訪問支持,意即使用其它數(shù)據(jù)庫的項(xiàng)目尚無法真正做到異步響應(yīng)(最關(guān)鍵的IO環(huán)節(jié)仍為線程同步)。

      在Java 7推出異步I/O庫,以及Servlet3.1增加了對異步I/O的支持之后,Tomcat等Servlet容器也隨后開始支持異步I/O,然后Spring WebMVC也增加了對Reactor庫的支持,在Spring MVC3.2版本已經(jīng)支持異步模式。至于Spring為何又推出一套WebFlux就不得而知了,應(yīng)該是為了打造更純粹更全面的框架。可參看 爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

      對比:非阻塞編程方面,Java推進(jìn)速度慢,目前的程度尚不能與幾年前的.Net相比,語法上,.Net的async/await相比類Promise語法更簡潔,Spring WebFlux在請求響應(yīng)處理上有一些亮點(diǎn)。NIO/WebFlux和async/await底層的線程調(diào)度模型大同小異。

      后記:C#8.0推出了Async Streams,應(yīng)該是在理念上借鑒了Reactive Streams。可參看 聊一聊C# 8.0中的await foreach


      其它

      幾個月前(美國當(dāng)?shù)貢r間9月25日),Oracle官方宣布 Java 11 (18.9 LTS) 正式發(fā)布。Java目前的版本發(fā)布策略是半年一版,每三年發(fā)布一個長期支持版本,Java 11 是自 Java 8 后的首個長期支持版本。目測Java 8 開始的很多特性都參考了C#,比如異步編程、Lambda、Stream、var等等,這是一個好的現(xiàn)象,相互學(xué)習(xí)才能進(jìn)步嘛。

      .Net的MVC模板引擎為默認(rèn)為razor,它是專一且多情的,依賴于后端代碼。而Java平臺常用的有很多,如FreeMarker,它獨(dú)立于任何框架,可以將它看作復(fù)雜版的string.format,用在mvc中就是string.format(v,m),輸出就是v模板綁定m數(shù)據(jù)后的html;還有Spring Boot自帶的thymeleaf,它由于使用了標(biāo)簽屬性做為語法,模版頁面可以直接用瀏覽器渲染,使得前端和后端可以并行開發(fā),竊以為這是兼顧便捷與運(yùn)行效率與SEO的最佳前后端分離開發(fā)利器。

      Java8開始,可以在Interface中定義靜態(tài)方法和默認(rèn)方法。在接口中,增加default方法, 是為了既有的成千上萬的Java類庫的類增加新的功能, 且不必對這些類重新進(jìn)行設(shè)計(類似于C#的擴(kuò)展方法,但靈活度低,耦合度高)。

      Java8的Optional有點(diǎn)類似于.NET的xxxx?,都是簡化是否為空判斷。

      Java ThreadLocal類似于.NET ThreadStaticAttribute,都是提供線程內(nèi)的局部變量[副本],這種變量在線程的生命周期內(nèi)起作用。

      Java

      Fork/Join:Java 7 引入,方便我們將任務(wù)拆成子任務(wù)并行執(zhí)行[并匯總結(jié)果后返回]。

      靜態(tài)引入:import static。導(dǎo)入靜態(tài)方法。

      使用匿名內(nèi)部類方式初始化對象:

      ArrayList<Student> stuList = new ArrayList<Student>() {
          {
              for (int i = 0; i < 100; i++) {
                  add(new Student("student" + i, random.nextInt(50) + 50));
              }
          }
      };

      可參看Java:雙括號初始化 /匿名內(nèi)部類初始化法

      Java 9 開始支持Http/2,關(guān)于Http/2的特點(diǎn)以及它相較于1.0、1.1版本的改進(jìn)可自行百度,總之效率上提升很大。

      Spring3.0引入了@Configuration。Instead of using the XML files, we can use plain Java classes to annotate the configurations by using the @Configuration annotation. If you annotate a class with @Configuration annotation, it indicates that the class is used for defining the beans using the @Bean annotation. This is very much similar to the <bean/> element in the spring XML configurations.當(dāng)然,xml配置和注解配置可以混用。我們?nèi)粢獜?fù)用它處定義的配置類,可使用@Import注解,它的作用類似于將多個XML配置文件導(dǎo)入到單個文件。

      Spring中的后置處理器BeanPostProcessor,用于在Spring容器中完成bean實(shí)例化、配置以及其他初始化方法前后要添加一些自己邏輯處理。Spring Security中還有個ObjectPostProcessor,可以用來修改或者替代通過Java方式配置創(chuàng)建的對象實(shí)例,可用在無法預(yù)先設(shè)置值如需要根據(jù)不同條件設(shè)置不同值的場景。

      @Value("#{}")與@Value("${}"):前者用于賦予bean字段直接計算的值(SpEL),后者用于賦予屬性文件中定義的屬性值。

      Servlet3.0開始,@WebServlet, @WebFilter, and @WebListener can be enabled by using @ServletComponentScan,不用在web.xml里面配置了。這無關(guān)Spring,而是Servlet容器特性。

      @Autowired是根據(jù)類型進(jìn)行自動裝配的。如果當(dāng)Spring上下文中存在不止一個UserDao類型的bean時,就會拋出BeanCreationException異常。我們可以使用@Qualifier指明要裝配的類型名稱來解決這個問題。

       

      其它參考資料:

      基于注解的Spring Security原理解析

       

      轉(zhuǎn)載請注明本文出處:http://www.rzrgm.cn/newton/p/9866506.html

      posted @ 2019-02-15 11:32  萊布尼茨  閱讀(5159)  評論(30)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产美女精品久久久久| 无码国产精品一区二区免费式芒果| 影音先锋大黄瓜视频| 成人精品久久一区二区三区| 亚洲av无码专区在线厂| 亚洲天堂av日韩精品| 乱人伦人妻精品一区二区| 亚洲综合精品第一页| 亚洲一区二区三区av无码| 国产精品自偷一区在线观看| 黑森林福利视频导航| 日韩精品一区二区三区在线观看 | 亚洲熟妇自偷自拍另类| 亚洲乱码中文字幕久久孕妇黑人| 亚洲欧洲日产国产 最新| 自拍偷在线精品自拍偷99| 久久精品国产99久久六动漫| 日韩av裸体在线播放| 日韩人妻一区中文字幕| 高清精品一区二区三区| 国产91色综合久久免费| 中国熟女仑乱hd| 亚洲一区二区| 欧美丰满熟妇vaideos| 高清不卡一区二区三区| 2021亚洲爆乳无码专区| 久久精品国产91精品亚洲| 中文人妻av高清一区二区| 精品一区二区三区无码视频| 日韩精品中文字幕国产一| 国产一区二区一卡二卡| 国产女人水真多18毛片18精品| 亚洲中文字幕亚洲中文精 | 又黄又无遮挡AAAAA毛片| 青青草无码免费一二三区| 欧美激情一区二区| 亚洲成a人在线播放www| 人妻在线中文字幕| 九九热在线视频只有精品| 97人人模人人爽人人喊网| 国产免费性感美女被插视频|