From 96c9961e8b573b19472a461937e881329e040bdd Mon Sep 17 00:00:00 2001 From: fuqingji <51312040@qq.com> Date: Wed, 27 Jul 2022 20:08:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98=E7=94=A8?= =?UTF-8?q?=E6=88=B7token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- source/img.png | Bin 0 -> 65761 bytes .../security/starter/common/Constants.java | 6 +- .../filter/TokenAuthenticationFilter.java | 9 +- .../starter/client/EbtpUserInfoClient.java | 14 ++- .../userinfo/starter/entity/CacheRole.java | 46 ++++++++ .../userinfo/starter/entity/CacheUser.java | 63 +++++++++++ .../EbtpUserInfoClientFallbackFactory.java | 3 +- .../service/impl/UserInfoServiceImpl.java | 99 ++++++++++++++++-- 9 files changed, 220 insertions(+), 23 deletions(-) create mode 100644 source/img.png create mode 100644 uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheRole.java create mode 100644 uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheUser.java diff --git a/README.md b/README.md index ebae60a..bb7c47f 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,5 @@ - add: kafka日志发送service添加@Async注解 `common.log.producer.OperationLogKafkaProducer` - add: 文件sdk新增修改文件名称接口 `cloud.attachment.sdk.api.AttachmentClient` - add: 新增角色-审查人员 `common.constant.EbtpRoleEnum` - - update-20220630-fuqj:修改SpringSecurity过滤器中获取人员信息接口,调用extend服务,extend服务实现在山分接口基础上扩展本地角色信息。 `cloud.security.starter.filter.TokenAuthenticationFilter` \ No newline at end of file + - update-20220630-fuqj:修改SpringSecurity过滤器中获取人员信息接口,调用extend服务,extend服务实现在山分接口基础上扩展本地角色信息。 `cloud.security.starter.filter.TokenAuthenticationFilter` + - update-20220727-fuqj:修改获取人员信息逻辑,把从山分查询出来的人员通过extend服务扩展覆盖后的信息,缓存到本地redis中。![img.png](source/img.png) \ No newline at end of file diff --git a/source/img.png b/source/img.png new file mode 100644 index 0000000000000000000000000000000000000000..5d183fe90c7b826a8fcd47ee6eec229639f72790 GIT binary patch literal 65761 zcmeFa2UOGPx;LtrP^C#zKtPZpRRIB|TR=nv=|~q9kSNi66$!IEzV(E=-4RAxkW)ucQ5%?;4a>43a)#Q(&J;Sr-pJ*vD~5{ zfyd1Dn_g7>tlGJekPvsFD{et8!r`uR&%;d@))U7cIyv;HX>>+R`(?!P_%lQgfTJQC2>iRpmkiQ)+}>V{H;M^d4(!A~d;CJofeLDU;G*9>O#u ztzeeD0K>Fv&4JhEqj;X8PGJY#abi18Y9ge7M}G)@4?CjL{DmpH3+B>J`!EVCOL%*gq#Y4}tkjZa zP!soIZ907q_lttpia1oL-C4Dr9>k`zytvWfY3C0+t0EgH*9;q)msv-=UQM|4mRR-U z*^Y;KBv0@r18;<0dV3>OG;4)7+EoD4k62ihB9v9|7}vQ-8II*PGkKD#0y)E$!Qyix z)ULV*68KgbGJp4u5KZzc!W{uJyPOl;x4ztR34xvUH&9U>5t37v4Jyv;42?t`;0BUt-kvfpY-F{^#G&c z0Q2_<;Zw4Q_2-rxWS|%5FJQng`9gBL8P?#>eT&jbCaj)6jVPSw6mFAbM+zGXjOMB; zO-b@wc3B|o+dih~8!982$QSAH*C-$2RvG-@_I)a@Kq<-(;+H*HyZGjOh7e=F_q3doE*c5eHaM-@NWf4)|OeE}ykABR3i;Z&Kka5C*Kt1&B zt5qiKRf;NPa-}J5{p<*Ybruk@GG(k_J-H~qD_Ka8ydP%c(l=K+Vr14uYB?k(W!Y?z82inqAt()!Es3{rYt(Dyl6XZ51@33cBMSy5l%{WyNlYfs*_5Rq$Vy zrXti^4tiZ4dYu5$*wA2Wb0f`&yzIdvHWCsNdwct|PN+ScXep~!FQMkNow@lZJtKr+ z8iG$uS{fWza3=$eV&6}lI=6R?<~UI-kJ&PbM)YBkitHN?iiaml-Xb8zM0j9nijmui zby)(X`L3cnt#kUHiU@CY=*fD+GC_E%udnxljA!DbRD6h4Ypz^_R($nmiAw;llneb# zBMkmEO)}vv3bKWA#17-HhsFMb9|`V)N)K^ zyQYug%l)+#ilcjKS|$A!`r|T5JS4y!_%X!TIdR;ntR-yNiBA4IhW#DG{(lg|{!Y|? zC+h!CBx-}aQc5n98rpy6QaO;ha!0m&xQ$u_BkzY3)jm6p)jW;>88$D2g-|*)R}7?0 zT=iI+F+rE%1(hUe%L3^W*`96)l5QAvou9(@n*2RFqPfp z)rVeqOZRI!O@05~)`kY;vXU{H$9(pWHD%-Zmua70=<4X8=sS9Gbk9TLqOq)LMklls zWn5g`M*(#-?em+b=dYw9Al$dWiyuIBvm%KHp0nbre^5tJq5}emhIBzjTlZ$+{ZWU5 z#7Q?M(bcio5p^WZMt!`xJsFy(Aw_7QQP9^G%-E4;NEfQE1TrM(@Ng+oR?J{r8MI)yRHQ#kfY!`RL5LU=-BnWh z(SFh-j}hT<_t6|Rjm2Zj%eL!?u&}VB*6OH@R6%EhyyY4rgKK01U374u4oIb-iQAKD zYgr{Z>fe_kVclP^L9K9@gu*5BTNjrmRE~Pn1L*EK6mZ`MU|K| z4*nSXfMs|HavW%koA~bj06Xmc6Ttlq$Wr zn;GTH2q*$8tZ_hd8dK^AOC(9tw&N|US8hpta+a* z$e(*8WMpI+NF^?W7qKH0G9HJfU}R?20y&YzhuATNQuY}3>X^>*Y%l)8>6;7{lG}68 z5vJ$vjf37vjBi2T7|G(+J%+=11HB-|kuPCqy$ULc9dF`tqkh=~Yc`=wzcwJ9u*GIr%g+&BI{PJ}J;+YwMa;@W0Mw=O^!bg{?&E|h3TmZHs{+w2{J zx}A}+*o4|@Ve^eQTLKn2eVzK4Y;+NTlhs3Of9Wr0pniUS8Xa=b3+kb)glIlHI;L+A zq(AZ91&ae%hiUEJY#8mG-7%(Y&|@l7t^z{CT2)P!!QRf}Cq{z=Q)@zauoQ16F88Iu z8n*k5UjnzWZLX*jgh!C+JEfsIpCiuaX&C(xgEN%eKpQtclKdV)_#ZGoJ|n{Lw}guS z>vR?O`g_1}X#eli4@shcn5jMWzs{KuSDEA8q>TJuCwkOplmG>m`@hdlU9$na-E;Q; zbxMuKwG$G^%KN|01EzJn0SxJ11gZZ76aD)L|3dry-*trNJ#Hbb2PB12rTN(9@O@Rt z|A~8o`z6S;QsSbh(x;tE=XO*6okRH7cz$U#Up9jd$BU(&d6I>qm^-pcc7wPAm@ zFxRCjqIOuv;km1Vf^TvB9>~b2WW#QB;oV&U{bO3VMlR3A&MBgoFJERLNtKra6<-KS zN=k}}iCww`H}r>6Fw)bjqHSlXa3tg2HQqEMNSe`%Gwsm6T+7RQu6?-GmW-0xd2+K2 zYU5RMGgJn>xn4=I-}$6S{i2!r#b+|MmX;^6a?go-435^hl%wRJd&9@Dis&lPO@SyuG<`t*!qgl7bKQpvGQk$`Yktk`4(H{sMmGTMVZvzt8UksoTO8))BILvSAr4 zN)&&|diNdk{>(~CYDW1QgVNsTRwzI(qpf5yFxtm zZoKQ-XHjDvfOl{zCrG$$EfX-ic^Rfn>|;lzpMED?av1B&Gr z*RaJ1weP(TA3fMC36WSONzg2Epo`YoX?OeX{@t`br;^nVgwGT zMoucCxpoz=7#$C)X+T#oxTG-1&2?)T@;O$|9b;6?O){__+rV2^)+VMEaHRJ@k?o`& zr-i>TIXI9z8~o)>3gZGkqgL)0Tg+7(${NL7YPYFD8x-lG+u#{(2;*q_gg%Kjg|Ho_ z0#mI8Wwgn?6eQ`#=v9IqfwxO9)V#vOWM@CgOmCa}fB@^k( zox(&^qR4p3#K{g?VO~^ZHQeZi3!5iNNyq8a8Dt^CF&ru{b6a3a$Z}T74-tmnooUx; zJHF>aOPujGG7b0V$REml_N|xz7p{-Q)we2P0V-K%puCAlFwQaUh31scamPtmedp;t zEI9jay>e37`Yjj7?Mb5WvXjlc5r*sPZg}dHVG1sHk;@#K#Y~B;HJEuy=hHXu;f;5u zAhDKug(5!WOOi8dhch7ugoy$bls9C;%<(1(D=K>-89b-Z_qT7pG?leEu0*D58^o!C z9kOqas$Ng2^CM@La9&<48ErT`r&^zM-EVVoke~(7M9!Eh?1=L>(k&{WWZ2H(1en?O zDBlqWVTpM7WPC~oM?R5sj9mG;22_~2RU1dX!eTAWP3XnN6Eub9^Lb>Tbj zv)`-V2GA%Je^r9{_&Sg zlCE=I>3fO(YY@}+gv+}4DBFX*GF7938ic6@-U;_*Gy(4Rn;3so^b*3Xd}~y$IG$;G zfJdP4x<3g#1sbSo zyjVs6q(&+uv#WRJf_eIAC2lr_7~N%cZhA>Pko@twBRA+%e^qkLZ@!l#m{UvK>7qkz z)l2UdNblAQcpSfJd4Nye4$s$h!Sk6oKi983NK6hBY=>VfnVQU6fiy!4ezew1VINOl z`YEW_n^FA|)qfF~L5a=Lr9xdflb$f@Cm);Qt%(nvEQuL$`*O`{C~BXpu$l zY-j4C#sGpSJRmF(wa2fE>LD5kAX#&v)~FO|uH^Hirru*`zDAXny-fDy4iy)IM(1A( z49!3n^ZXCgZu^8!@HAc$tNvCm21}5M7qgqF^&v?sXrjG?EY6aS9{MmoUSMCJoW6Cc z{9?vdVfw_h1ztDJ$hlb1CFQd6{7d~IF-SBBihnJJt6~O=(+r@8-)JTs+NsiRi-^Oy z@#Q9+NdiQQ{yv}S|SRQ28$d4pp@T>EdQd7#i0||{y?Va0T!8C9(v*TYSzDK2#;>a7>yzW zToB*#UMfj0HE$3s)5}U&SQs+@!Y~bw_IZjCJ@^8_M`#aCFv{#!m1WSTsJ1JvX8ABq zOINwORFMPXPKqXA&Tal>uT7MW~f}=tPXD-{KLlW^a3XbL`_s$Vi7q zE*7Z+^sY7HH-4x&)`7ZJ7U&(Bm^5V>HeKIBZ7LRvEpnzs*wdOj%1fC7zO28N`CrzR8=tP(QKNZKWbOtj@nA&%QinhfEq=G;wY#ZZ*rs+Q` z0~EZ)Mrvha$J6(?X11BK@u3_1m2rP~uXIBFeo_gi-2Aoj1QKxzyuW}n z{xk;gHeLT?q3vIE+|eA->uakCAy(o|57vkKM0Z@oltS2u1&Vm-iyWaez7tRfO!2)Ci))* zH}DR+ERc-_B;%yWVhf#8V8^LLyYs!j;l>M(%{4-(2@VPcG%}s0gCuw%H6+I z%Kry1{%;A{|1{hFHY)t4{Q9@N1AmrxJ=P$Nj-y_f(syOX*wY5pUguGL66oyn|Ea;^ zPooe^U%*?(52JogX1fv6LmH7H4!Ed9!Tb6@bw&L7JA;NiV}e9TFQgB8e`*o%YJRv~ zKSg5A@}C-0{;by6e@_zIq>uyWzWHAh`M+UH`LSwR(KdmMVr;dkfa)<~IPwFe#rpdC zzE4eEAe5B22wA(c^i&7vL z`d})ogr1zrovo&UJxEMOu3A32qP)krSQ`^sWo#~0tOL{=2dC&rDu+?EE{Kdp{905p494@p{Ag_4^ zw#jQ?bQVZvyf```&6JKVIYjEf4)Yto*Pkc7pABF3b^!7PVwpqW3@ZIV4*A6)bP-W9 z^pu1wF}regaP+`Y!4~*^XgeOp%3P`C$6SWb0Uy}U9fzjA@)tS( z<6Fn(y!u@sPFmk(GC>lyTtRXRQUyIiewH&vX_SzJzk92HR8IdJx9}B#@aW!@XwV8( z*9|o_wXI?3dBg}_Ea+3^ox3rC94e6C8pi&x8~Aw-|G;w9bRrg4fJe*hPZ}Ofp4vaX zdWU0%5BW(L{`9%lem1D?S$=Dk`lB}P-*MDQAg_4rZB0vI%Ov3Cpdw$^6=nU${$+Y- z3By@Kf9tdR*UkRFm2GY^b<|As>e(xShFsi<4CTP~kLyr5=X8^b3-7lEvwz)F|9d_} z>Jzbvd*3-ZJa(3xP(h=n=l;*Vt4qi$_EW(MNG5>T?n#mSp4OFXJceH&fyuJy z>!wpcB+^+9i{-kPE#95)YQE|~FLPQ4Dg_n&N_Q3EH>@&15$&RxKa zN=kYwf6!Qz75jn`Gs9|$h<&+!RCdCojDrd7qucI0KJ*7QrwK{5!;|Jirgay8!L^{W z+1W4*AEUH?OG5^JiuLM}6c=nUs+obhvi`Qq=m}KmilN6t)Msmz#j7-xns40aZ=R;a zdOqV~5E~}CMCkk@6I9NXsEv@5@t-u*>o2jNTzOOI)0N=K#UO>sF9a!3OGJZ0%Hgrp z@NUo6i;(!k-o&$8o`?HrNrFZ>&d1YX0|-)lVFv}sq&ph|TP0Dx=$ZJnC(IE$B3qf1 zX%vNh!T2_qvWkeGzeJh5b#$Y(MwwZXob=%7&IIGL2`nWDCqG$C&rGHr&S?R^?{m-yu0PF> zu+NK0l?$6@*6DW<8VMS>W-cHh;W5YcE&5dy*1?$Rt*!w4s=M7mUX-tm%PN%`>C|Fm ze4@l^Ek$J@9ajq4>ped(AKTmjI1-bH64FwFu&pgMhbg9Vx*yHJJh*Ii;_&%hXQC0W zs60?||702#Y`%Bcode9Rt0YQD*FIr*d27nbCY-Y<5H;jDa*_ceqG-pq@*`-Uo^*%J zEHqQOcKMDb^i|vLaE(-GG_~xOUnKit$-}N#Qn4Qm~Iv^Q-fC~23 zTzLqan6jL)tWE+afz}=Ga`~%(fPnP$3th8#ywH{jgV^*izpwEnWA5+yS|gW+>}H!V zD^mQAEG;d8>*N4u6Op>XsRaO0k+Q zC|TM~b}wt9g~5`s@5WLvQGzAPXI#7pl6$|vXvefOw>dzy_19;ZT!?AHv zzSw6MKn=@J@Z_xdO=pr?R-is!4huH4mEm`us_nA3AsgL%R{w~Qoix&Y>4ouD+zW5L z_d4kN%e0m^I(VIXM`U43Z(sBpwfTt_o~V`7rW-j&Tyuf>w)N2bF_`2cl#|1hES}zT zAw_oA;$xNM<^+oyp&Rwiau&6%Xm0Hs?H2a&IJj=c%T4`CEeS8`qa z(ll52NkzWwZHKhdnqUYw5tFdc2hPsUZf=YKL(*>NSgL&!Q-}>K7P}8yXB`=atm?+i zseYs2>uAz#Rl_a5$M!~?2+WKzTT>O&hN=ZWV#~2Cm3+3KJKJ4l#R8z?R>4EayooG zCO1ESt#9naxXE~(G0DPua)R zDF;1CmB%DJz={DBAm^qfBoe|L~+2rOLR>0)IzayRVpQZyt|Jk=-K!YZqILWn?_;58LyTT%ivp{KhX} zQ_g~@A^=YMQGbxaB`srR`j;UBFL@;joy>%?^Lfn&nWYcMMr2RZ=O|r}Kz~U3^9QoQ zJy4FM`}~4-P!+*tct%Z1rH!W>HHOCOQt{@B=yG*QC!Jj_(psPA4wM_}VGp50tq8j?+$xvrtp@2g9YzCgK>(m`ut(S2LE(<9=N^L0B zf@xA;UmCI1FDE~oG(cH*o#Op^3k)a7`{&e!-v!Ve5%^c`gyGjzHU(JlhH1pYv5OIO z%z4>f;iRlzp7JG`;j;D_?G&F3#^!q+@Bd;8EpRJ_;={OeDTymf zrGpOtt~xkVuYxxDztCD(Bwl$L;`IU-#MPPJ>d#u?MZ8}WRlYNd9U4;#u}OMz(I!_j z*E-jCGpDs+mU{*dMh<30RBM6TuY?qNXO8zs?ShzxxeErp!gTPuVnjsylD4F_LgmQ3 z`q6L!A-RQr4jl-7+U2>CiJ*SH2bPupe{zkOTjw}_p>EbJ%o z4Qx)ZO-9Ln!aWz`TZp6ra%|AV(sg#jMD6p4?evI|q_LH8yka$W4}+U8L4q~n6NV22 zgC3ETy|i$sS}cFLLbTD^mGRKb293`{lu68EY3QXur1@9dcL{8t!O&OHghh34hYm<{ zK;Wu}x$$H_?C7bAciVU`ywU&lu~s2ZET7EIo?uIQs9cXff6{_%K4e&P1%*5B0{z|q zZvW+>l(f(x1k;`yLt~qKE)kCuylH&9eIo?QpV2%^`iuQD0sB{3DG4LDY#`Mw0+>l$ z8W$)Pv>cae#INgiWl1{IGUG#WcaWRx9-bp!QEsW*R@asqfv@CYTY7@1_F*bnu-oz{ z#`0_EL#8|0ladT&zurV@==AILDsodMHxCO*{g+>4#yk9k3KUGa>qfkP0`TTf$cmhj zmAf*B+R>0DR`5t7QYY7y=1{p`-!j;CHusFEWjA=Exc!rMp}-JX<&|tQv`^F_y+EW= ztUq_V<`$ARO7m{qo2rd;5=T3lXD8T}@xMFtqZ$ZhVbf*Zy<2zrB`8 zF^8GCt<*blI4*oQ#I7$em;@>EOy3XKSMA;0YN-=io9*j*z5jZ6%w$p7mvWj;(^*T| zYj({h#NEyKsa^Vr=v|IJZ=%dfQ#R=vun3v`mO46LjwmK~hm#xE+t>A6fjX`Wgdx0k<6G7pPc@fJM6hlnn`2rckMF025dF*J~>-2xTyK7QZefqtULzU((KIpf3U3zC+ zp49LJ^gYSivg!1RX;WwEk;Ne9@pr~Y>-YM4A%q)#`$s1IULUD{p{(RJo`8+MJ?k8{ z^UMPFu&ca2r^IOw?dX;Diuyf0doI?fmK)RGME(VwfI-c}H#=p>_eRwBJnwEnwiqp~ztovOO5{}OENHQbkE_O%$?m3-U*7uCKth$w zv$)ri8Z9&FnOcohNrrk}8olDetFchXmT@XD^Ty>q!rTi+-eW!_&m#;$|B{Ovqf{V8 z@sN%3ry}T0xJ*PVB+%4z%}}pCGdZYNCgx!?lUYl#haz%-swmSr!{N3f*FCMw$7ww2 zh-ZtRRXnnLk~S6VdoDcJVSmC#D)QJ^Q46R*j@Ea~1jZa*(gqddF`qP^M)mhHULFi2 zQjaQheufN-a;VRrNs#$;XOQ`9!?$cU0n9vt?4I`Vt$zN>k+b14Xza1@hc@w*g(jvi zEGW|CXq@XGbE+&WXWT6i2u57BeWya_N^=t}0_Qs+H#r))AQ@d6j?K~+)thbFWOE)T z4QGDaLC#w06rpq*X;e$lS^eOa`fC|0w#N=tO32&R6+$+-T3*A~l#yfXPw4NxmdPp< zSB@I=G~5iX*(Kj3N7BLQc6gCDSpdYVpe6OrU)Jn z>9o;N?^j&fyKJ{;5p~s^@Rdx~u$YK+=fU!D%L=ip*Oxi+-~f}c#Hm_#d!(Vk-X6iW zri_-Bel^u)T~fQ&U9Fsk{G2P-vexkDOoE8M%n=(J#O$dG*hAG;=x=VNUNO*W{EVp; zQr*_(@_Slo@S(IgLd;{z*KzBZ)af30A`kF-egMP85bpl9QI;zdlI}a}PN?1Cily@T zp)z|PCu1X05W3IUnen=8zW9=OSZ>j3z$ApUY+z3t;ece)`E`1om7su>mH}c41Fp9PvV9|VieSK#>=MV_0R(o`9 z(D##~#tJ1RBrHRmJ|vt}{VYE$8j zs!5y@=k!e74$`5Gm5kh8&BJVj3HOO9GoOZ&`iR6oB*zUGDj-lY(hrg&yGOIo3`VhC zr|1ec8aGV~ld+OkpW(V#yo>)#ur^T}jAPm?(PTGXl=U5(m|!KW4FQAK0uxB8_f}g* zcSo>Gs6&tF$g$oYD8?n}QKE(mY1{79!T!X_j|oiQO+9y4t=7)z13|Lr63sNz%uf15 zynB5KO-=*1ug>EYD!Qr9nUXPKL;HIaF!I>Lr)l+%ncreZU>H)-q2=#k$}dAg-Y`Ws zSW(Zurps%Age++$!O@dc$F_Mv=DUcWLhNM@pvBdh~&6t6ok8i@xB9D#l#z} zkk!2{U2(3pyw>((uZn5c(j~a>?`jvmX09e-(>y3depw7DqRP8Z)HT4m&6YUUz7aBK zS-7Fd-fuOYHNEmB_mP<6g7$&oYtI!r&*PjJr&0IbIHtfm%F2ePPhZ_$nNi#Ivvxb# zD_yI}1Ly9sS;ltKiAOz(;r*odt)r-L&4pAw4_%Dl+Z^8x2v8`zDN15l)X7@}^ZJaI+}_r>KS?S=XDQtGY@1`_jvcui(0T*a3ahqQ=)C?3K-hIs z5L>(NXfF4gCSI2BVctVCy8$(w=iW+V2;&vRNPk$rzxhSdCSvkeLyz~#HSJtO8#Sg< z+@!p1?;u+f-<$*SmiKf|K=it@$PM^rwFR~IahSE1%Bfq@V3G%9ZM# z_&s~FOg?(qGd0jj{8FfBU$l=EYSQcaGn~lYnoKL1LE-g7YwHcVtlp1$CwyR9I#+gL ztH`-^6P(_jc(&*zlc%_1JjhUxa5p4ghK6=@Iiu_j(b-9c#9I*rDO{82S8C(hCDM*{ zF#p8JqC61;lAbk4^M|@vkMSw_y|7U}(ht@)k|80!4zzT1pD(L`9>vwcpOy+zhoduc zJT@{1OKg}=pKcm;+-pr4Og#AhzR?cd83qnjzHS14#En}AfR!y?4DnoDGc5?OhYaAL#lX03zS#Dm%8@jBb!7?L}Yk&&a~mxo)X1e5!h~98 z1Qj~(;5{0N$!hGc7vNe~!i!%n3iEW*@Q8>RYOWS&=d}?0kbR~c=KTWy4VegwWy9w_ z@5yLMe^_Rps-M}FFC?)fcfcf?GNbWuRSZ4XUVw%$yFZvo`AX8+eB*J-{TnP>6F~yB z%KSh`np(Z=BVajLytq_>@x3gJs4B-N<~U{h*zZg|n%s1)?1r6nuGP2onvWX36EJ5` zOB@;%a5B|5T{xszi+OA(979=LfhF5?B#$fr5I-a5%+~e<5T|bBc z87<1vUkM#mFLZr#FYf_ggK8-m4Ts7{0d^jqk*tq0BSt0@XJt&d9|5Uz89p)gs!=u^ z=)}JF%8b+9URh;Kd)K=DjIZ<(^8tf zOzSKt9~&UnP7mE5*FVKYeX)#P#e(7UjzWR%bZR+6yzb#D=d0d;LXkpm7zon`;uR}v ziHCc@DRBm;cAI!9zErx=qYH*bmJB-%0&RA};Tr027qCiYH>LJxl|84$N6RLA4EN?@ z$QQ1r`tb==HP6P%#LOpf=g^ypz?%ncJf*L`Nq*ddeL~LKAvZtdQoCjC6g~6th@~p_s>g~@qBhNaOq+P~BC7s5LvWckgWbl7Z)mKY+4VlY#8JA{q zAyQv?SN_#u*qL<7&6!#KUNwPYU+{HmW>#qR$_tyldDOvtj^}QziNNLp*=QmyQ@Q$H zI>yy=NIA;_xd6#1HCl0PonXHwQcu&=lhe;DvL^W$t2m%WAlJY_waEklgAl;gF%9l( zI+uaarzEC*x#J2}UHQY?3$g%WMla(2Jb>C%Z%<{j9jr+o3Q0Bel+9&iR%5F$@G(#6 zr>#<%f&<{BB*mPmpG|^B3q^mbTPopL?Mk2bR7pB>v-iU1;tWXhUX}B`ZjpIlg=lp1 z*GcwMdu5f`4lkchfGu0wdJT9_T5g`(De>Gb*(%kUE=b&8Aop1Ih8@h0hAeGG)ysPZ zDpu~0va2kvF-K3g>Sd#Y==|43;DX(G8_$_5URLW>>N0cLEWXR3MIE;21nyo@nHhca zTIMWlc!XeyFGM|!FuhZ!@=4p>SB$69it0*2A7)iOsbY4wl+v6IEz*(vuo#jerT1D! zzpZ{%$yf@-P;V9Cd2M>0r9I(u(LK|bG$?_&;!SY@J-BH2cr=mmqK&dQL0 z@MBm%S_H*iq&?jI2DX-ssW0em3*=VFaeng%IxV%JDf1W#oLDBH;X0GO_akOEdhRuf4wd#MR4m6z z>I>O_0A+$7N7RD3o&sLC!+@4sh&ga9#BdB8(!_`sxJt%t4p%u*U``O=D7~RcneA7a z*FGV(C@vfetIPy~PwB%oX=P8hsSf!l*QccFEA%NK+@A~y%sLN`jnfIDUyEz1zDUJB zyy;grEmIK2!A)r*Y)?vUSrZW<(mI%S_TJr3OuPgyg*OqUEys_*bXj6H=j}b!H4ts= zK(x`O!+m!_7cJnq-LjRO|FSFrmt7A>7jybdyUR(Zh%S!y?f2ZUD-Jb8hqtq?Lbww- zzpuf;QN6ipuHb@Kl(ifO-+RkBcUaeIYORX5F%1|>XHhYkMf7cv4T}9b>AmZ2Lmy5w zl2`6b8yAFyw$WeTo4f@S6Ddq5{oyO2tfhc&u( z_%<)DEk#*+sC&^3F*Ee4cOy^b%B0^67$PS1Omq7UYWqzi6KtXB6`O74#Y=QiolgOpq`X; zTZd?r$KUCl<-lKNW2d6k(=4ixf}QFVN4${qhb)(cw{ zYg?(EbkjU1o{6VCHP0)Cn|P`(U|>8P5ko z`8m<_Do`A6b>;6cGe{q-gaBI|d-OaQE3@%E=GaALFU`1n(Jr?xy%3}#50i;uGTVHP zK2W~g$$jKfag{e%3FNG>QpM{Yl+_nBh&{(7|(VIucDd}pAydb0}Sj!E)kLY%o{96*-q3-r@deh-`< ztT@@cyu4MhtQUEWi28{mUiTXQAVKjdOP)e@dw`uMwd}t5An9KDe0AZIiU%lf(k!ye zrg9GEa+b=bnZz_qq38ro6(`ii2OOsLJi%k^u%$;M(!ymBPuj!A*SC=i2dg0FTf7&k zxEUtnKC5WB(^FFI04#2aVs^EakWP}V*Lm`L55@X7nwzYYdw2Dnr}7&oqP+IiY*;fU zV69l+?c$_UY47GsHfa2W5~Z&coaAGE>kQRANXQEeXHR>fObh=dmH+Vf>aTw z!z5Af(9@sy?>r2D1|$lqFIhz@c8b83bEBH5(r_j}&s9#()jH;b<@D5q=9XJ*ePNL1 z!ij#={AHyn5y>#om}4_v-H54zPhiwgm)?8FwD;M)-o{)Cm$4e$%H`am8jg(uHhJ5e zn+Y=RQ$mT`JH4m_U~4qPE)Cb8e(PRLpiyZaMU_^2S;?aQ{dV`7&sBO(6^&k*to``x zbOC`S?MmGMcx6#LW4ov&JCG{fio-uNX?#{Mxh`> zCX4-jv3D1Oi#DgOF?G4vS8G0IUsF7;?)_Xw!qi96ur%#o}yf;RWQ?)s;zP{ct`ra+(0UosGpJ)jAQVUP}>pLc# zDl@7+w{Jh?X~Z9d6rcQ{yIsN#*otQ^s8@IKZVp3U^@~nY0u+P;zSHJZT>yMs>FMby zwP9wW7vO@-38{Ct-FQ$PRa*yjne8ya&eCRZ0^3sc{MDzj{B8`XebQtL zMk!jB(oS``-8}*brufID7 z==@Z+>stg+ZonC|KO-;gy1Q6n6{Q4xdLqy2!(v__FAzJBQoEvJM!p{oRk%_{y6CP9 zaIF>ZofvQ58?eRgc|9(B2)K=t90H4_9Z-NdOP};xC6S|1E-He|b$F$tF3=^67k*!dJgKK)e_4P1pe5&VR3fr)^PbCtBH}=JkH^=O^=^Ek`m+*Q5{K#9 z44D$}W_dkN22S{#BdK~Svq;NMGjis0-Xl6?L(P#}0Exw`5WC?w^YP=yjt-UajU^<6 zn^Q;c%2U^=S9H3L=HEw}3(j};5vljeb;E1%2agxaRL zy;OCN&#Tm5iE+Q@dMQ-!MY%z`#ZySobclMBF7Op@p~9=;ziJeBP^D3E-c`Beq<|M8 zBw4Z7^J#WBfuYcIhg)}BS<=k}DKFMPdS%dR3HKdZ;qoA*dkT!RWk7E~SxL z4L*7>P3GtOte0yqv^D7`=>WTU1&fyk1Zh8C>MejkjIj?@ygY<7A216RaMbJERshM` z);wZ+v}odas&lu79j!WJnpol&2r>3PqqrkbFqHRsQ*mk1*pw9UFd?%UxB8ML;>k;z zyCv>DIIj|)G80>VnwMJfeui@i)d?5x7cu1O0k|xEUPVXH6m!CHF!;pVvCRKH)0`5*G*V$r-9Gw_9Eg%}->|D1mb{Aq4%u zS;0}%{riTYk;HYpvoQem>pbYHr!{=_&j9$1WPtbF4SAQqe zBpYs|f1)U5Fs$Sm)6_&T?np#o7x$GXhHI?e#LL8Z_3XLxb@mtL{f;xMWrsJHfglI7 zSzQ`E*>_W2m~^3r^3Fa6{jsPLyK;W@6rkDXrUCEzxyCGiHzG;gabv-W&9ca`F`I-% z3`jdzX?}WQ1kKP^i+07Zr&G)xYq~3#sQ(Y?6dX#!Ud zzRSXtmj?jYJ>%G6gy_!d9k7vrxd`1qBCP>ATy%=74ro9E>zCh`>c9&u``{0qcw?6WZDHi@mh(0w*W%@a_H<7dxn2j#AU zAxi&>k~q?3{2+xFhmOCotJBN^W(Q90=G_#Z2vZCikis9Wu<<&oA~9h=36U;#g9t=O zeld}v^>z5Gv*L9vNk;L;pFgkZS%L+6t}#;Z3ViTHcOBTmpIGYm%#<1>XyUpjbLXL`B4;eC{Wv^No<(na(b- zZ~gZ}&b0%6Iyt}6CPWX%npg6P+cPpBXTWLe(j{(Gd<}zSbo6_yeOz~MClxtR{7^2U z&7&=fh;D07;{T-7^h{BD{%iJBgT#VX#Tpy4C zl}Lp98N8bTMpppGL)g4KvV?y6i_GA~yRq7d=89DQ;rs~xn&%O44FpZyd^x4O>>Kcb zBMtul{2-Fwljw;!68&{?Ar>vnepv7VccS!~cQ+g57pva#Rrg38z$!6ELLWV{dv8A9 zLY|zx&FSa5i8c8=5=-z-x(G9oZm*UrDmpM&XqghbK;!vK%NbT+jo`=L34-K~@z+B$A`VZ957vJaG`cf;P zKF%M_%X_c{SQyD!aX$p5@zC}|;{qt<%Azd94a0zj8JP9k(*Xr%kmL#H>sseVy&Yn7 z!3Psul#n=?83@Z9BT8V1bGOAHr$@h(?XRo>^^RB4%0PH6m0qAbIX@7anaZln6Ug?3 z0BDt-GA<#Zp-KY;y0t)dIPOzvbeE&r$@_5{QJmWJ5Y_>_pF)e-i2L=1|vs&dg~mWRKSapeewAU zD=iGn>Aw0V$RX@L$Dns`K7ZJ-4}r!xkEC#pQ)w{@Fmm&tcSZ676Z=Y`fz0`8blB^K zgyP6oga)xOXWtmOKJ^sHQSnmtZZGuwr0*S~kf8`h58Pz~7UzvzpVu;ZMzdLCwMU6m92lFKX2VI^Z#DSREp_VT;m-Z?Wzv4WVU zilw+L{6^+XWaMDZ**E1Ty*IIj51^Rm*gDl*%awPQ^e4^cfPHOO4!F4IjDa%ZaTr8C zHIm6aPh;NcS?wERN^J_y9`4_&(tn$=!9cMX&6SA7Pr61_F;?|Jfe)mME&6Se5o@Fq2^U zEpIul5{O-So!(#Em(l2l8yn@1^s7y5CG7n65JkilJov1!$1Zr;+h1B8sx5ndL(sBk zXlBhnYjBZQB?vhFUw@C^U-OjBehWb~Xw7S*EkIhYdXdb#V&KfT0(<=a2TH+@=j^W+ zdae)k8a3{Ix(eMQ!Wq6UsuVr*)hwuQf_MwWl9<;*M7Su+BxN-M#$FyjZcUYQTklId zSXrB8s!e?1gf-e}u1xgUFX(@?+ti( zOr=ZbnDAH#`A@vRj0x!!3>D2o8zjUN`VF{GAfHA=Nv-u)eDW=@er>5S$vHRco7Lf! zR3eM2OL_dF+nMJ^s>nurkPzD}Zd^mezKGV8YW_=CR{A?Z#s~{Hw{Roob%y_pm#PIz zok0>&D>zv}YyDa1tli{iv?=ms&>6nQ1K2uYtlZIWU(KSSQXm_=qp?5{h)y#q-s-@u zn}A_R*nc!a3{W4y$n)3_rhJ`+;5G+vo1nrN9>pohmHFu(P5r{R-XIs%w4lafyUI4TJ71=6=UpyVDEo2DGlEx1I+1^ zzCX-tXC7J_TPgh%G701&HR;W(|7b)QXsrj7o^5B(z6Z6_IbPm6{Km{;1G=kqAFY^rsNT1aztGwnQM@8uOGU@lK0Pe#%03S10d zAf!skh|X})?S6qctR-|S7ubySqye|FqL>FckU>c?QPDFDIpDKfB>VshXP`ffd*htg z=wyrMv@*hs*vPOqcB00jy~_?ri?evl%N7|Q%!uhzP;EL~LJFsjeEvMH)aJ;M8<^>f zbW146%07e4xy~+^YTGOM;Db)$$U4DsgvhjCLzsX$F}+jqBTU|dQuK$bM2ZYKJ1N4r zs=*t}k2zR0bYH;S%))W>979fXP>keM3yDo4jErt~CKJR6{O-wzVK5Awii%H!mb<&h zkKFG*gH!e485{6D6JY^q2u!Kx)?#$RS_{V$?7L{J^82Z257a}$6_~OhqTG*%5y5Ug zz;DzP(0`kO;tUnz1NH(kqi%jav3%Ym}ccgrh0Xw#i+mVyMhi>=aEUU62 z6&R!_Zx2Dfavf}Yfyfm@r7?^#(e19JE;;1oyL%>BhD3hJA&7P+I3^{=XsvN;y3u^= z7Ml0VoWhUr6B9G-AwJaNh|U}(J$r8B{z`(%FyNfkEfW8s5lKF>Co9Ti4hs~QP>Azjnq)}I z^9$4vB`shI4X~@_d6a%6tRpb#JW7aJc5UcBMkxky*!}1nxEB6%4=o{u{++?^z+l5f zJuyUi0JE&MK0A1Ff^(N-3rf`G?msdiqoBVFW?*_6&OE+z2!AVRV63DaMBY9GDOOVr zCifd8t7Y)d5hbGA-K^rK4rj-IKJxO*{(dG?T>)-rn9Q{0z$Mg+8*Y>o6(gQBh)ArQ zUIq+I?i~MUnV1=ALCA9mDMw!^FQfjaXjBDZe{SS(N0FOx4I6lM#O_B{9ST*kFVuzR=&&Plo?|4P2&#_`tutkf4zkj4K>I*z~j)4D7sfo1N_L zTk5KK;R=NOrv(=JTXh>pAmVGcGhisZAAU`nS`rx%doqmd9@tO0;Pp|!I)7kK4!49?i~_IrKV@T^JFFCkYPX9;kWMu zZ;?oA|55b#`Bx}K}a19lGnH3YZ8&!ZPa zJALYyFb|_2JGTk*hki1!#{@?tXM%5n&mV{U|5pD0t^99o<#Gh$Gnw0+96#8};Nt~E zB>pT0X`g!bRgUJ1Eo~aV4%F}0rc2_db1n*#>?xE_jlqIPwXRlx~1>tkmGtc%c z*B1U>E{&a(IA>o%_~Dli!Jm4v21L#MWat&BUBH7!ha_zj9fAa_!jniJ8PJ|X*leB41{`j*0>7LJdlKOYTX zvb)d4$W7Cr(!LuEz{lTUw4aD!3MJ@I5rToke(ah~AJ3iiA;&+X?}o{7&eesT?gCx4 zK2Ij-A-{e_2wwrJYLH2waBv{GS>c&{WPBqYt}l)Z8@n(0{;ANI7MOmN-g42OIS*@2+u8*H0j<*Ig3Bpd%ym@0t65* z?bxz)Hv)C_yd5}2U$Ll2u`A(M`!_b%tq_2`a7(fv=qS-UH;#^ zMn%xPK91)5lHz~1%LDdBo^l|Te00m=20EtXP(leQ9-IC&wup^D*O5MpS{m+qJofU> z_JJ-cn0s=x-tp|>MEtj@*3Z1gqNve_GPL{sB5&^4adU^Vf~-~|^^c)V%uDpAV1t4> zcZ0H+7ara&i~+`ErPc9(PskdB)v67%nyRJ0)=gr`rN>!4v*NQf)5w& zGYmgo!K2A-U|Vp_h8J_L=Mft|USF}XuwzO|NdcZzNU~d;2E#nmlFY! zN{|&B#UManGp~cgsS+r@`bB0aHHqq#fmRqB^qccXtiYDe&d-uI2 z>G8EvhU%N!LGnsm13kE4sL)V4y4Q*%@~_y;LxgzD8~4@N*}9c$@-N+XNrv%9N}7+I zaFS(OK#Tzkb?J3)p637{s8IzF4U^0;6sMr0Zl^}F%APVS7m19HX5GE(#0mVb+ls*y ztW!vw8O#x%PNOlA$d zOM*w$gx`c&Km8KO6Uf^iwy?auB1#4%j?MidAfcD6Ox>rLSx|8Hl$m{p4EULVaf!K& zQAI~!XlUr|=4A$O0Sjn%{R9Ev^jTV11Y+`-wNW1o|P+`{ee65uZWn3!w(bk4w@m zDW9F6?#doJlV9W{M@ZrsW!<|2{K}{dsJM@qL97O+WhJ6tP;`SQ*S?qcNFRqB$jk2? z3UjuH00;(F`Du(4>F4y$p4z?OCP+Q7hb1x&VedZ&>5@7%4alhRv(uvwukU_lyq!Jr z1-xh^#9RM93N?I=U2U~?0+Pp~^~sb8 zgwmfKduXzLmmV9VM!|0Y=>KPc|DOT=Kbi6WWXAu3mm3GM>I~j!n>W2=;e(3c<_730 zV%=_VvQYmE(!jUfLQLtt`v`EJzl=Np1S15UE+7SO9m(o3=u>~R#` zZ;hK2cNUV*7G}_;Seo2+dXpi9z~o&L-M@QRc6K(nXe7w80n`9A*dj=jl~Az)on->Ab^W<7@jxyIdha&mIQWT)9@c*e^yXcD^JfSv{1 z1G!)49t0^RO9^O#0wI`&28dA9}sHS;_4un8DJYXo2xCwq18M6rmPu3n16dO|t z{%8yl*d0%Z$z2rm>;MoclX(r%MkjJ==$d#g1J16687m4Rs#R?{{-f&6QEXGY_uSmJ9Z(VCx3wqY$seaCDCeUYKpu22qxa!=%X$-D+ z>oGDhNz!q4mNjaJ(k&A2_0iKMq(0EV!4ZaX-JB=bir|7g+y2vqmwWW9UU%XvP0x%; zBo6bfF7yOpM9m{`&+TlOkf@r*)y?|)t#uJEU-jr^2H||WPG8lPK^De?moGzR{4F}$ z1OffHbq7PJlnBYfPp@@N(c(Tuivp8YaHf8f%llXaQ!_mNPW=cyna>~#tIC1QNx5Gg z7#nC333F*Qqn&4ga31k{VzR^JJRef%G^%io!tS~8dM!?T^ERn^=T+Q%ds201Tl~9B zdl|DEpwLi(SdnsTz~xz0KHC)9Q@kEmN?mT!Oehe^>dL}*AEo5Oxo?EvmdjlYDEYL1 zR6!$CwYpmnjKn2>UJK1HR-3`dRkTwn8lLo}*2?3W3aAuVj&&KK5$LC+^OVXpg^sRKh+7 z%vvBk3c4uAY=&q_5qOVx^y_wiuWa7aj@EC|R7Ds-4cQe%)9|}%1LD~Ymc%Cb0|El3 zZ&pPz@||K?!TJm+5FO(OC7(Aye@#v;c*Ayf$Tvj2*JIoW$+ug$IIFM9R88Ftb$Z%YoH@>K%~!lpS7-gs%q~ zXVzF_NajvCEMz{3XJCusHoQ`TIA)K5b%f{3l88fFeH+i!3+`QE-M<>-a;*iDyuS>c z$D%h~*UgYG|Lcp8kq()BL#np-s$lnI&UB`?vHryrOitd*no%A9sbdjZb+C;z>&1>i z&?zUUMdEKYMccv~QhIWR;YLsMw2X_G#CW@_R-+z70@dYfbgh3EU2hra-?{ZJ_oiYl{sn=ds;lE%NhwT8!_Ii0TE@KV3JpqG@&u{@0;|6!`7{ichA&Lp zH!kQ*?{DsY*uXfg<6;2h0jbY+`TA^*Hj~(N?wQf4HZK~0UXtC|yNv@mIY-CF0KEaQ z+!IxNwNxLVsXh%7s|m&VG}fpEt0OUm$N6?W+26NX{*J+`^wl(}8Jw7Y&J%o(3$K z`MQ16wZp^103OZQ5cm5&MeDMrHin#als>smycccCxp5*VqVsCLvv)4iyl9f_D&G}R z!87~qra<2XrQ<~pN`bL0x?JS!28(#9891(lP*;@pKdfRg)IkY>QDV;t5)3g^T(dUO z^S1`}jURNTCGzrKo!C_z+7|`@sPsz|3^ry10H}-nwH`Wr1u9oe7Je2$R&X|Fpl?$H z&}YD$j4x!3E6z6@W0;NYM5ZgfReD&r_hpy~*JFA`;4~Kc z4&E8>(8{5)qUhC^i?|a!i zy(7vIfF8Tn4=(vmlFa0&g}1qH7qDHk%Da91z!QV6I82Q#P6M9%coXVK!!26nf*oFQ zR*#wXpODfnCY_n>TvyKbok4ND(QOS|nb&nHCYiBYOGs~=#uBT9sv)$qdVRGC=y(SN z47b@9ETF1qpSUoAe;RJ=-puZKO3P7Z|86UNpYA(Zw_-vB^H((`PHb zl;)`dEQ(ghp{Lxz2A`7>N<3uGV7!*%dMRh~Q{J00m)lL}5pGuf_kN365-7RKM$+dq z5~i@1Pn(UzGwck5XH!3!%P7l|vWiegL@QAK{D+PvYB|6zz-{tJy-0bR9Z^Pg>dWAGU0GOny+Cz~1KYgX;f z2j_Q*ULELPYA=3s3aT@4h0^7@8#+U%fVI%{$Q`B zf8C4Zz=s=-&Ifguwd*?4pjz_SVg0o{`8C}R(2O%OG`XIXT_=6R%Mw?aBhuqjU#7gF zGsBc;2me@qhOGTV9n18oMUh0cjblD&g|l-q`|*$wOYqHooHZVwV~SgIr;> z<62f;Ha6QpYtKVNtk#~Ych_H@<|`1ciwru_api8CedJ;MQ6+~^X4T6JfItj@Qo20A zY`{156~|MnuO%pd8R6{tpjurASUQHr&K&VcmYPfA`)v@C?EXp1(P~ff5P%s-@$98s z)+q7$`MpKLobg?v)&qH-i>W}ZLpV)2O)1-H!et7Tyy{-9t-N~(xpP?({6$J?BcoS7 zXpR0hOF0tq?6RuYA#KTr1;lduZxlACxP^PaKW4P|5QtlvyieDC;wCTfG7{rUFwAo- zcMtQU48NAUl;^w&ps4+hcEodb=ZDa9`imocILEPUlgZ3V231a35LYr+r4topoF} z23zu!8De-yICuG|-4O>K-(-i|mZl?==lu-5oV1I*96@9YPlkz*(x=QXq%-vTko5cF ztNGXY;2E?Q(9r{5NoxK&-ZR6Aa^=BfU3jgqvNu-o!Y7-24h-v9~lvk46#HnWT5kS1TQ+Iiul9|X1et~isnApQ!@Lmbgy{T+LR~= zhR-q)6ch}{r7|Ls8P+68E|ACt;~SJjmEo-r$31w7-%q^gv-6Nz0sbIm9NiEtH!j$M zh{QuNWvv?KKC&SlzP>PcaQ!sQE89}1$Aeay%q|F#L8?_;Kh);DvCRnVxl3?hduPTcPS;=7tf;%-mEdah<$7 zEXiN9pJEzCv&#GIGdTFKYV9vkEh>{s<3&+Jh)-=){;oO{ls5=%GCfdHj&?b_C9jQz( z#@Wojq$g|^tr@#6VI0Gx`@Iq|vk>B3())l-YZnczs@X!>TnG;DjZTraf5=?;G)XmU zy7=IGW2d=faUX}nx=CmqPQoVgm=>(tDsw^mRI_kT`&4gruw0@K^P`j&pGeoVaUm&w zX4GY()uRs(g4oEW%N&a5*B0;NxtLSA3;Kb6L-p;!TSnp|AKD>z9tRDFT5X8)cR(z+ zDkg)Fk;oPQYgqDHy8+z0$EjW8Dq4x03drXHxwZ|VwF(nS(T$zLMh8aHN7Ls7Ep0LK z8LYQAW#0DCf=59FTFysl;`~@&dKa25Ur1V5h2bH@PcXuSxE^`ns zLK5EZS1mr(wYsib=f`^dOMI0ol*VSR|bxIpFqg%fqR-sdK zL!N_Y(ErR1h)!#>!|O!xQIyxmAwO1ONI+8TKmmD@K3ZGXytKIHd?{J1zGzXDgD{Oz zaNNv$09wz!+sD7aYsp)%7*v^H5+ymGy0?R~0K%i?n``{CwZtv)fx^43w#_c}g5L2F zVa+X->XREjogvj;Vpkp%r@}KTS_h{DEr!-BHJu*uuR>d^soPkh-$Iht>ujnwbNbS= zFhHbxIL_s^f`JJ1vE-$$hdvnF4~GH{>KPW8c{gvx3f}c~Z+ZcUrw-ahI#1UuG3<1gpy${t6HeiLwGNpF zqm!q4tf9L6*yMVF*mTAiKAk#`2SDw-r(x%amO-n(*TSRdlDV>tUA{Yw=;?1!^6il}J|hp1 z$P~?dH)9=TsNCZ-8IyGCUY$lCgzqP_=q20NZgfj_s+v>g5qT>D{jKm<+Stx3^vhBe z-@_Z~s>=o{+k4?^n9OpI%T=Of;33v$#Ohd0r|SWP@8a%D;|6x6!lI(O8+1lx1umws zVoO?{hZ$O)r4yMK?5Y*W8tHu`9eq*1zY`lL+R%e&GAYEE`i~Y*V&jGnaF24F$9!~O z>{}~bd`{`dX-lcYh!fV3UVEqB8;@L+KrSY!drnFWtTjZ~m05%+wmE}ZyjdNmk2~<- z>k}QzsuRFFW8PyHH>KnoS$0ym?+x=w_gVimnjJZf8eS?KN$`>~>sa!h+d4l)(J!n4 zut4vFmnMs-jNGn}Ixq9O2p_4068x@s`!}BqP@@BNs0GPM{2PK#uJbBsxoi4M;h>06 z8{&=q2sVYc^JHFL-qu3Im}nF!mWuz@DPi;h?)<_UwtE0qPd-1M#_ z&rdIcy)XB9-L$&?kFcTu}&C%(e z{JALjY$kCi;^V0dSci-@<)OBe=-#-;ZPst4e|zlf$9BZ~s5q4{>l4#wn@!Va#`f|> zE|j@Yim>D8&V36vVNWi$ZjgNgWW@3M>sNG5J3bRTJ(3~kI*T?NW<4w*Gr{O`AG{eG z(@bA&g7oHuOfj(P+#X?#rQ5S5i*+Vj$M?_TXVFgt zSvvqybZZ&>$3fTLPdLfdWSmWhUoJfQ^fhUhjU+rXi$S(|Qu`uF*?lOFgHpijgP$Cu zH<&lySeqiny%$Bj%}8)7Yi0vpmKC`09rZXP-3T153+bK7_2I2z))3obUdl7G>7gc# zN;+!CjxP7gpS zX*Y4D-rV(tCYp7#9s<+IN{b|PgMmKMW3`giwr^gZb)AZzO0=ukHQi`E!FxRc7=huk zbF}V`T)4eDnw6}fe9@+@C{5ooj1HE&FGujKK*Rzq_XXvLGUVpq9V()%0K29(J&*9< zCd(|mSN*-uBIzRv>PDn6nIW44`O;I~J-L@=`nehl)%$1HC+`JR4fsANXYwnH^`0m7 zmO&niD6nfN^W0!@AAdo?5C#qv6UNPQGq2`0tsrKO+Ja=PuVl%6Z;D3s7~?K?X+h|# zoADho)lEU$tiykS#cLND`mfncKu`dI18>aE&%9OMk=Y7I?Cc!qoEGh0oz8BII_;LV zZZvW_tr{5+%VBc93kK`X>?eF^JofH;(q25z{KUwm>zdSJ zINovVxobZdZ&ylTcb6Zu2Qy>J7x4X4fm~4;1CfP=g%|ES!mgiRZb$h(-g$o`yrtT} zqToW-?kF#bBI|4sfpp1-S}$#`62&$8+YjqlM>NvZD}8oK4_2pSrE=Z0R}iE9m= zV6a}(8=!{8VzFSru2GYc90Eg{gaAh;y=qf3#}RW17@->jl%g>6Kqw3N1f6-z+*W(~ z0~ZHJoX1A9nd6m4z#XVZ8hq+FVuO2t`cSLyT$!0K;bnipi!#qkajxLwUR`Em_lY>x zjn0}hr;fIcqvkzNVQ3o z^zQjIJPxgLIa;WOvY|MlfvL=7eXto5uLP%(z`mJAHRr6Y`lQBrv6J2l$pd0r@xW9J zv!r{g&7u#b2YNCkeQTIY+6{!Avc(38Hs>6uaL zx}9nFz!r9B9yJz^6;`HQtvNa4FJ3KnZWS&>bHk5n{SJ$iM*(&eaR01pJ;==-FjH-= zDv;LJ;w)H_B$hqcCI6guy4t(%gr&RFjMp!RFm4@=Wv)2rVc4&AkXj`snKX=No`re$ zu3V4?HL+MLZt*4~@B)80eV&6CWa945o+evUo0&7J3<^7T=ie-A1Y#jN>m7`?J3XT* z#{GCYcL?y&hW*+n@m??o_jU-r+SR=niLM*;k7OK^yZ1NyM?J~MFSthP+9Ea7z1ZR zz4UrT7cphafuut1x$7yVUN7|PoX^E1JDcT}Z+se`Ojro5(@63lp4Csbw7DqaFd}D_ z%^I+&H|>7{xpuSy-ZcB5U9w=gQo;-UkZG@afwW!kV^{P@Y8`JALoz&CU2?e3%) zSsJ!bUeB#JHH;=#YuOXA&e6G`pNghS5p%6SL$1qGKF!bk9m}a? zyUK*MU}qalrBgHy=Jux**pYd^7wO|Gz$Q{fuyl!aYmgO0k-}~}HFL4m@(btgSP5QF z=BxugSsFPrTZUy0!pOX-;_H1=tFkVOsCqj9p=vu;b0^6* zWRa5XP((6ZMpgK@)fY^i=ONQmLYks_u<*q~MnX5~Bc$t2E&eUQV39wu3~6MXt8he` ztX$h*2N^|<^oQeN$u32q69!OC&x;$D7+t#%Vy8Ir5z1&oAw_-cvgQN+h~jy*NU=){ z&Hd+0Arb&>#%gUv8W6{~(u zx_~bM02bhE*58Jk{jaY6u7;Y?!k4V@O~Bah5Sh^53sHT zW!6C9#sqtH3o{qli?eSzcv7JL`jsBL{4HMR+*H6*nOs4)Uk`u6OEhyTw>n{JiK3!A zCH8D~t%;P!%ApYVGJ=*Dbx=niNYb$qX&P{`8cRrhr?{`T4vapL%FByPeitb}6(B*EOndi%mni zb07?Gw@pkk1TWZ{QmCvvt8c!rw(h=1i+^6XRZVrQ!9?uXd2C}yLt9u-8}D61Aq5lD zM_K)ju8qyrbjiAs?*(2z6sof^Zof4W^D4@z`tq_|LQ;K7A~rOo-V%`a9@ldx&ecw% zRs?RkyDgnfj0i~xmFZ&r2*bo~mKK}DZS-{^%Y^m%^Vd{%8{KQQr#xsedt>PMYd`Zq zX3GP{hUq>JkIsEgzlBM|26VQ3R8|b)d+g){9>xacUtkf9R|@e@xi^0A$O!dI4`G~K z!X$j7g8O~)-ocs+X^I4-4)>AN`yNXF>-rn_BbkE-`=~W_t_xz<5h*LlBe5webr+gS zVa~ky$KA*Pb+$K|8IUWQ^@6)vc=N^qKcac%-12WD=gU5&wg_uHtWWN0P91G;jA8MS zETEPy_4YTK*>vR?QM~Y&g_hg(;3K%~hJP`uM9+NBYbCi65>1SZY)8+l66@N&f-w`t zwx$CJ9)DwElDlLSQIUqH7kU+z%RhJokmZ6rNI9>EGCBwG037Rp?{T24_@#YOdqiU- zSEcRT+2LX1SA3XWim=ejZhg1l&n<QJ5Hxd$g2k*92&6E86V`iC=zG-AIQk+#&UByk0SN8{b{HD&c? zBoq1m0qlYes%|(cV@F5-XS!LXVF zn%F|uakGs#3OkC7nR4n8G3;%%M^*TSdXIZb0L!4Tpp>bE1_2y;Eoqao#od}zWP@|Y zAlI%_l2S}5gMLtw%oope@R*rrhbwd#dk-(fAD41DcHOy0KiNQAb6OvJHf5bto-{7= zB;YQgP)<)+7aI+g^=-|5V)lEUlzE^Vmr#29F9`l?b`3ltuSloZ*)H`B9!j{SSA~iy zIKC$|N?|Vy6G{v#@=(*&lgoQWZMQZIhn+J3D;YYmm~zJCY1!i24fTEf=j+RkZD8W_ zN1r~2q%38T7B`ga;So!N)Y=K?^LW3VZ9lz`l3uEDZ1h0lg1u~eQW>o99-K*FAG`nY zjcd3)<#?k0->F0tfJ($_=yFtDNG-C=%OUACaY~0$VO#BbwtFy*v=5IiXLWM%8&oO6 z(+nI-a+Oy!pMyn_s^hhihJVD?`@1yFoXBT(?&LL70q}Qel?v-aXrv#R1~>lRlQ57n%<0*(wj0U*NdV1ZYoU zcH5Qh%F)jjRtjhmJq=j-r)vwduqgs;!Y@>wF}I~WdJ28a)SL>#S>jZW$6JmSq+Q30 z^`@~yD#%E;@xZ`M%I2(due)B%Fo|uA%25kYUqb!odpwjW9I##%HTILTeE{uc&i4e+ zfzjD?k$R#{$E9DaEQmwc$1?s=%qD-2$x?%KhStOpJ;LS!p@`>KczZJ@uGV+KP((FY*gSDZgx^ASpgSo(ikE!TDWLA4xTp1{3@O=2f0kp0{TQgjt(dayO* zdF<<5ggVssO6JQr_E$Ug?&CK|?HuYCaLH#iMSwo)cHAJXbVT7saRPE&+x7iblle4u zE*pMJ|3khaCyY^yV(^Tq_dPNA5|9hNGV||f4!#IKPLib=8p4-nD9o@=QvZ$sBS&z` z*u6V>zu1OZ(x?1Dz?_|byKxlgG|!V;t5*UIXAT4z3vFHP)@6;@Q6^+n zB1v|a%v(`cN9X2Xdl8vh1E2Ter@)Bf)8{+WtVJ`K!UVG(kwAe`fJA8@JvN=IVST{G zMlYLn^w}y)PUjq##`5dnve;SN9cR-7=)IBh3s(}LB52Tp+@pj+RGz;VeFwePj5|45 z1%$O5ePN|Q_WJvT^f=@0t5>g%Bgk$J(l@OX-n30b?AcMtA2a8;6R0YfcK8{{42%&K zy&xC)PtNLw0*R|?6R@sYMQt?hXn$jE;x>FTj~y)u`d^PpN}hwWmxKRJbeiYJ&{D6N zpj!X>OqfG+8ca8vw`Haof00h@j3a@P=E%;id?TiIf;<3rMl55`;?^C3D(C_zg6R8G z_}DPA>#3-Lasb_KpFR0EZJ*vkN45Q@0Vczs3%?u;g2p*{3<8E=Mf5ISyvR6$mr5|B z;^H_6+yc|KrUP+gZjc!T%Eyp{<+vuW=yTWZ+f@pR^8MtBSX?*@| zOC{AN7WpCo-+|A?36?BkyoEv}EGNB2@Ao;Vx9gj7r$)g&^)Mo08X7; zC{kIW2{~TK9KbWl7kTbFf2yzx;c{NiHR35??wX7 z?EbBoOOIDFVE@=^0_~9s8cY>Pgnf9xql*{w7!AKBu>U$R7H_#iJ?$Y&Y6yfn|JZ{A z>>MC1rJ(~pFh}@h!-wVtaSMV*M7eeYQbl}-=Yr!mfkgAm4*4Z9G2TPuKilG&Oz0~P zZVl!ramUexIzda-hoT{HL}pRAMGpM09NDfpqk@4>bF@_eOiIPXPXv z_zIEpJX*F$iAlmR10wM6?*Wty7Y2V!@Fw0MUf8E- zp_ca=2#W5()0a^^_CP_rEsg$VB|eH8($E3$>{%@UhaToY@{7ehKs_+!)1!}Ight$y zFbMG}|AU3iXKq6Wm%fu5e?jc&<$4e|FjGJC(kql*aIxT)=uz|L`d4%`T$bxR<)!f@A|#fcIo9 zq5}h=FolgleS=~<&@iI~9EzICw2NYbwIDMRRgf7jGs+~M@ke1Xn}URnqMIb35lOoV z`2BotSHfe7WHc!8T?g|CY>bT6V-cU;Qs_%e0VkjP`+AYyNR=1>u3-2kq&v_wOeU$T zFPcBUm&VQds1J$JemQ*W%64}WxVYh2@J_2XmC4K4HzNE!>ALB5mbAfyo&t?11zWk2 zp^WIQ{91!1&|QqC&fJrkO0R?f&Bs8Dc|*Zk9yA}*?IsQ(vkL$nI|NG*fX0AWw0w@X zpXw#g4KU5Fo9Ft}!XZ2tUe&1$G*;ysB@^)xU_8hHnEYNHdw2I|AdDi_Z^+;6J9J|n zjaoHRffC$ATq;>q6{&iJ;3xo`j-e!{3@#cQ8ygxPd47y9cy>o@sm*Tx%D`qxLx8UVKfybqgMotS@uAkkwlgjYN3TwRx zpxxEsO=?Bvc1%DFs^~KU&{TfEyVY=J95{X7N#3}=$dZ=!io=|X5!~4Vg5qURGg$Bi z`qr^8_EiFMlnhk#P6HiBAY9FOz*W$2<9CQF+v|%75?N$=$FjJ_PH?u5;ZNNgvXj*O z0J!afp^U$2M#=;t=3i`0yvZ!#?8{nksv|pF=#QYy)`p z0l@s-M)_yvJYTjp6lw}A`Dis;{108DjA!HiYSY{#IrgLOi@)WJw~g^`L5>m3@h0T& zXOn7D@B3;4{n0$?r+W84Bd-GpT7SEOBM*sd@@*#xMnDx!dqvF4T-647NRsrzv z=_^q95wG!gj0HbKiL>|Zv7;&ih2d4_g#YY0@@t;4Ef@wuwVYz;H{8a5WUGJ2x$iRs zJd7gO^iO9s3bVgFxmwSJi>u@Pv#X0b%&3UO&IB{_u%TUk*tzm|{Qb7iLnySY=FLGj zCfBOFSqUFCX$68~MF&FvVzij%-*^tPi~V%ubGOeK zqEr#xA|>HEB%XL15*55p=sIL{3{e`8qH$y9Dgd+SvqeTmqa+Ie9}v18_xkDMKqkZ!Ct93%v&m zED**cIe&qE%HWUHZ{pNZOgB=raDBw< za=T@Vm8Ujda}cT|1XoBrJOYNGuNCJig0`I*4Q~m|&;tj>fguP*MZOrv;kSkUDtb*L zQ~2Ne$~Y99gupe8?I3C;N~&LGrv5l^YOG$1CzWjnulGEnbzQtN-*05^j?3{4f=OnBR|JmyrJbX zNK(H#=*N#Ro^ld9>2sWik@FXvF1MrA2fW*AR1T#2j{Lcp12;cYAI~Ac?AV`E16W}G zyhGWS%=#2!{m6nl$a%kt9Zoc@To3XA4&S!;{gs3CAl}dH2ls4Dd__x0^{1`d0N1(A z)BHa^_$%k!FC5TZ$zlT-W$F&ni*2M}AoPbd`QK1zFE};!l>gf_)7N10AGqoMfA#@6 z^9||s)dBF8hb~I8#ULCsaz;e3KR?!)It}big28}Ayacml7eRt~ce8BUCsz)!xVV@j zis(B;Htqh!)SJA$VqXq!yP?yJ`ZUPA{iU&ksVS|G?!;7+tEs7h@Cc$V4%>(;8GQzP zp-Fvxo}!&t;r60rEMoSD0K3a^SQbX9mIB*>cOQEQ>LiP$rqUPT7o2Kp_%abLuF0?> zV23%;Jjkw+#gLN-n*{$%^PqqdzD+04;0ae_Rh2?NT;sxge1vTgxQ6+h2>a1aielNO zxE?220X7Fx8>}fy+Kd@0c;OL!Kvb8?tG;V`~w4 zcPJ8!x$b6v2jsJBKw{6c&1e@^LkZrmo{ygWtk2h%qf(4LuI2j6(dfy3D0lypB)@yH!=tne8EE3@Va8ILdntx6%+W3aFHbmp`_tXLulInI_o1- zQ&)GD%zOMV!1#AiFVKsTq!Y-BTdj$>ma+|QxXyw?7`~YCyIk`o)aBp0B#WfG(_DY) zl~D7bH>09+gvacSAXH?(1`bbQ9cC(lKQeXr3B(hLmY6GX1cQacw1te_zK7G6L;F2nlSvl@=rBk|3-!)F1w+V4yZj{He;`~5loOmN6#ICarY(^oG0HR z8W;lOIop#jxb?48ie7Qwz-_J+^L#Sd8ZjCFjb(d%c{&5X#b_C5Fy@@j`(JsLF6_&h zTLWDkJ;knb*^N>vx(nMBhAv9&Ths~i2x01f=c+Vx*d@?X@`BQHeE8iX1KlLlS9Q7H zZRK)mEYPa&2h8ukaBjd*ZG{sf`6dS5izCar-n~gir!lWVrPa#d#$aaXlj!T-UK3@4 zl1p~D0!$EYS(n{rwKn8VwB!}s6lm#7sfp(G2eT+Vx+1;m_!?T&FF)E=|4dBq@_feU zK$7Q;X|Et|RCBf*xM8_q6%zev!=R@lIx?ev&d$bFdSMpljDBFXwNo{Abt$GHRSji2CdXmZ|&AhqCo?886D;ycEUP zNQBrlzinB^`{;k&_pv<%;gl{i0tM$s1MSpo-VyOlPgbZL7#2a^THQHoXM)P zEI&zTxsQk{i#4s@#&bX^x^0;vx5W0XoCwcye6RVsFL{6*?_lt|dR_^79sNAl&H|&= zQRdvVyZ@+k;`i3)i+haVA3A%0m;d*yZ)i<6Y4Cl4gy|9682Bt|xdnM~1H*sPtyw3} zZopKUZRO%JL7?~bqfP(pmg7#n-HtbVao~<;3+5HxixfBr*;JMpJuw01Tb-pt)*$u2 zK6f%TjB>_J z+DO3BpGQ2JOFLNh{Hj3rSqYEg&6w!~;pAC~In?s`v*dHzs+tWt585=XPTB>65v_;GzjeV~dA#=?TOOKh^Go+m?PGC%djqe&=ZUPrzPFC>^8oSTn zq?xFkpjv4NIQ{AAm76Y#JM4skD(2d?2?SXNHE72eOm*=^zkk2mh-#fJ@uw#tViQ{uE>sMOK)HRJT~O9^FEp`gl|hj~)+8 zfQcb#9%^5PS@t07ejK5e4hxYtex<)wi^ZpC)vl`_piX!rQ9l`aHI`8$?L(W8_3 zKA+8Jznn(O_7<0BB=Xn5jf7za^7$Lpnki2ZH4q#rz|DL?65=DCmx-J|5_2Uqq4VvjYbi9^NtXn9R6Fep26VekuV+>%-9ike8bs6fdg<-(R;Ab{3 z?!Fn(ahcd+C*JVyPKmo$n*CNerk4uLt0LOE1Ln=Z6ym>?3FAu?-?K3}sfDcn=FGf= zTF$LOPF&;dHtn8oCY>fdE8Q%zEiqhLqNDqqOaW<)4ADs1)l=@)X(bft%REPl<+y7u z^*>91J|5P6{8P_`%WyNV3Ak7g2H0$)`&*EX#;65fI-5`RUN?u?ZEk1db;n;HYXe7p z90FCB-xMu~YY2qt>tA&AcD5=Cjdq*ykbk2xhO#pyM7&Jq>lUWWGBRhVqV&Y%O8Ya%e;HT@QzkBhXmO6 zor-Pe4Us|NhGZj%7YkQxuB~0e9pGk=F6=m>(l^x;=kna4{`eS=FF%c$1(9~e_j^Ns z5J5CeHkH14%B>W}hQ=FPl3vNNsq)}Yj9KqOQr@;l)E{=BVUPD^A*7hor{IBPw?_k- zp8b#N+<_}iaAa+L-K(Vcp|$WGeW#9?Ef60qzPew$5R$yRq<6fno=rnpO{b8kKg!uBAm+>vQ%2gr}}R zJr4#PsKvak1x75!J4W<{PdlWe)6OTyYaMae{z2w~KexPqbWySqvJfvf{oIlXn9kpg ziuxYRNlN!(AOG(Wht~>Cl6bEX)obQB+x_?n&OMW{84%VGnEiB=_)xyK+q;KWxzK%{ zf%Q{SScN2lYl)k2D8YJxN8xY7g6}OVd7AX5jkJ1mJLo&lTu;iY_|AzkC+g!+sJ@@_ zK?EAx;W}3W#u;c_9p?J&h|dSBm}i4Oh5M3Hz0AIOAeNAhj*c?Vpy&kisb28s#PRE6 z-(ROUaE7QlPs6H@#wG5=M!BKQG|IQnI%vUqO5!EEmUd7mW4&4fm?q03b^nd@MV!a1B&mf$$+~H-aql@=Ivu97Y z81VJvn&UrE`mWuzEh#*y#Q1H;{Kk1PINXlt1a9L~N%e`F^1o>wc> zFJ!a}`*e@zq)0lCmm=MvkNaK~wY?#?3O>=0dl;J09*rAZPxsDimL`v?h18ab78h+U zz9EmWyE!oJuRL&p@q5uP0g`Ox7)TQsmplVHZo_oilON~+zmVgZS>B2LF=Mf84(cY( zF~?7{y%gor7~y^La)V%#@)Nk(Ynh4Q`|;nSbC4n}dMh~k6)g3z58K5L8&lvFu3%73 zmBz_umuLytgrxjd(o1fOseC2K;`g0cz#1;2{W4r98CPUDW8^$LD{^LrJP`OF{?nU6 zZYD>+a6E0%|kx!X>a=WeIS`RJvM#q@tu|bmfPkSXoI0DCHux1iJ%|-j{8;!IIBN=9T8U%Q|U-F*HJyk4w=#wkGO~DZw=de zzdTHRUq;xZT`M@>kZ5_K#h`!prIWp4w22)iG4i**w{5MqO&SA?KX*#Pb@ELIG6^#I zX`MU>5CnC)%~w_3+VlXaz_ed``z#ZHsk~&n^Y4&%yCx!Xr*{DlyK{<7vlQUx1R@j$ zv_)!VDJ+h^ZF@zYjXoXgAlJDt!}@OaY*OdB?M;G zB7c3-jMnR6>>evuR!MdwX=XaNy^j8fr{eZ}0x+M#4kby!g3&hDh0N>tqF^pAVgDK6 zR7*~??an|~RaNOho|w23 z-nA$ho8bE*VR;wM#jwpjeJTGI`0-V~_R6d8loZ>R&zA{-|0^;*@hSS8Xx~zEh}Y5@ z7~=RsqA>)RFr408YLQ-V4gm(vo%Vt;Oc=IRT>s{)=vU^I-9{miyS(;MWFciq^Dfbl z?kCLyw*iD`*=3<4BO}$-iXZk@ei+|-sJrKly;vk;UnLmic>U8*_3dUl6OeQwQYwNm_i5=Op{HOvrCw7XH#t5*)=?5o0;Nwe1cOqCzAG5! zM93@jWMKu)et7d}FyWHHaHATxu1wxut6;HNm7WZ3-SE*`R8;N1;IO9^E@|_aLB^(p zvykJy&@80Z>uD%u@$Ks~sI#E|_!t7Cbm%DFts8qU>(Od?9P%SRJ91fhNgQLFTVN0g zZnUyFwam!dcPfH(KioYQWItyZ3I5mJ`M<$?81J#@f6xY+_s=603&jMuaai zdH4JMUwdaBmUOoN@g{L$$Z~1G7Q-~Ra;;3w6lq*a&80GPLv5PTvP4G{6P3iw-MU(8 z<-(+?)z6&FewuEUK zetph!I476{PPlg8gv{P|#bTOfyw2X29v-si5~T zlPw58h+PCxM8LnDU)LXo2@uj25UEYZ#F!L?YTF5!hTme>qn>7lq6^?D$o~n0bI*h84A---Djem*0;%!IJf-%>J=l%zpaA$XB%XP1txlRxsBju_ zI-6KwD*v2WdhGR@M##GpqW#6y&DjjL{F`} zgt@jbWJJ@q2B%(-31w@`y>Tl?3?tvN!m30gF31KSUnuyaFFA%?jL*eBWsf#KxAr`! zIB|~z{SP~h8bUAeXYP|a#VIZOfO^lGV0Zq0{qAX5d?kIuy^{F4tv|jij>*P`JRxkI zyYsl_fbkZqJjALAz4mRj+&8M5DK^4~0d?MIAo_VxBGz_Sc5DQDC_9&fz(@YfFDQEN z)1b@98?N948aU{fxH9C!n+)Ab8<%Ni5h?KPzu?d&uGfu-O*VE47JX(gejB(nGn2PZ zZmTfy?x1xu9y<1hQF8%oWwe2Mfnw8M@)ujq4ub|HO$I;0Q-7V+nWXo?)l3GpO zed*@&k56>HEX7NGcTUd9N8U8v7*eOF{R6xPiw4vW1bsqIoFD_EudJwBgaR7 z2tMp{oP>#m8!}6@WD8Edl7Iek;HW4jk|t2oGq2qyDM4Xpv9(vN(Yzq;;=V6<{H;-HlKw;FOzM=^udKPQ&`t>HLCu@6ziu$~Ldt|5JuW z)fGmsi?fOA+-8CCiIyZ|W7>)jD-0}8Cpj3pi9+*Oa5Xeci{7Xq|9qBOcPFYYoHsjq zTnNO8d3^=vRKr@25je#KS6r6?^Vz>rdf&LOtNcOTbTy;DDK=-CbPyLf;GWTuSY@IU zVMxzRfrBBMXESQfE*<6SLBF^np01apM~q74uA85mr9z8m*1jPZTX$?GwDq)f%%TkG z+Sg?^Qvz1bYChp2nr!y}#nEJNy{(syrq|>om65?7yOi2TJT+3!2_R3EcTjgFIJ(e( zdFJ>WwZ61odQTTIg~#3_HQk$#{aX<^jZh>yhRY?)$qNs*d`a7T7&T}9x$S9h%TS^K zuNy)7Uf}&-_dtD@bp|%t0qolzuy45k!>gEEZ@)ywpJ}*}6{c~XSG~X6t7=$E1zz~- za$qRqvBkmY6n@gfh?-?!9ZgGjAxHBm=rq(LNoew;qa));tge6pc8{6>6Lx%yvJ zWdvWJv_zEw(AzvwVa;Z*(JfYI9)G_R_8HlEU`bh3j3d4aR#Q(^d&ayib3w4({1=1C zVFoT|NmnQ8*J4Cdep5$mZyluwT9P>5I3c^u^CX+x5G`dgV~;@AI?l9Vj)k{X&mHRg zl0lB-7yPoMt#{C&!WQI*5b(nVg3drjvFEbSx3(cVG%E&z^%=nzxBf@ne2!axc}b&N zw5WkDE~5AN)0Ar_cNeDdok)}uvVHmIo88;+@3{?`! z?+&t9A#FsC>BYfKO4T65YeYpvT%;SxYN%9}0yB4qjQI7e8@oVi;g~a6>)k@JSoZG< zKD36*xq4oGsjCn(E9@Gs2X;M}(~PcCB_<^$fl}V;Q9=ZxZJ>Jal&~*$Lhh_(0*C*A zY!k21Wqjo#kH^!@)1dCXZCURi$S6Kp<++>eLB`fu*pq+j=y0yy2P$b*+r4AK1Oy_e zm;Wb~(^DCtK*C=s#Rz7@q93kbgawN@Jn>;d0XeX{>QGGWuYpEW`j!ds4#Vz1rR|f5 zv+UZM8d(^o@e~TXzxvnd1aIC@;N;+-8m*wXBDWk8z6U*<3EEjm4=5Ac18V^^4Qn_nwZ9ZVN%Egy6MuD~ZOUM@h|X|nEUMA>^5}DEM9#1i z9e^LXAkR7&c4J?o)Up>N{+Na{e>L6PNBo`ds2EfU{NEdo^Im>I$r9PS>C`Up(|WEV zOA6BZTI%M|G0ClMH=jtdM5WBTFCs8qr$+Oo!jmRi;9Lk(aj(chEz{Y&ehZx8hO*6) zPD@!^UQ_!86cvc(QyiAs#pS_ld!|cBA>DP=bTlmrWu?XmxGzw1{|tfjT2sEr9e#Vp z*1RdVTv;=SD!05p7%*v?w2Q|Ou&4Lyjv?+QGG{VoUGpu~JZ?-$di1UM=rl+9i{kNu zUFoVPtu5cLg7#O}NLCHbmllqI+uGaawIG)l9(abT4FhSy%Le*Ckq4k^e`;>h`9i|n z0JAZ!Vc`Q7QGvk4sd_Y^>I)YZMzT9mk9nq*y-32gn3-F%{ese`%uzz@PD@s0QBtJ{ zo_YS6{TmbaLh5yGwkt#P9dcz4ZTY4kYV&;O!VI_A59L!G$Gt@%@JEF)gh=5B0;}~B zyYn_v#33{_uR@)kJdY#@hu#apb7647L(v94DxvVp__2vmvqXS*G>ML@O3}m}>706$(YfzqL)8`uG8GrH-c0iJZw(um;(%9mjx*RAkts&=A*=y~2B4zMd!m$e^ zd5!Up-RGuBFRPc1FnrXE7ZWJoKq5o&xi1OQ=`U|A6|EyAcY{*`12KvQ+sfIHr-M#w zkkq0FOOgqHh&*nq0LsJc6lyWI;;UXLQLoGPsG--{b$#ToYYt==9BdqOA%!PS5V*cw zXX(FBgG;qSy@kx(0l`m9^2*vf(J)Y8#D*#m63HHYCJvKsqZ-bC%o}9b@%*;9q0(*BA{}$G*nskLzw< zG2`RH#OE+$@ZPlhu%W79>f@iFKk)bA-X=G)Uig)mwuiZOf!FRFe=>eC6>EaHbZ*(< zudZiy4lHw=(NPg0wP^(-T=Eeqt&oJlC^si;-uJ*#@=G>SjlD|5{AE@;04%{yL8T@V z;X^S9dL~XZffcDxXR${XADSF#t&^P2Fr^hp!O6)yN3m0BdlrazCk`u&@4TWwo_Xp% zj8bn{ne|j@m@*vOLo*+yL`Ft#K*iV&~cjaA~r;*dC;@;(Qp?W@ct)Q(giTgFk&KKxH_j%JTt- z!vX*Ae?~J0d-aEz-{s8;=0243hg;HsCjvN^v$AAcArz%YnD_A1C?5DQUwY)g8K|8J z)L9)3J8Xnmt(n)8GnoBB^yt1E)XNNJw^k_~^a!XSfjY}?hAgJYphS1KTb+fhM)Ihh zxYUEao?!jaUvRQ#-HvPQKiBu$x^iJ=uuS&L;N79CgjoezkpAWxIdj;`=G=)rMu9PJ z!~X$m(G~3YtpEdiA5wu_Ygs-yrux9HTexWQTfymtS8 thm8HqPfjmew(P=Q8}z~d9!j1Seba>=qGi5a1M0SHudTDq?cM&wzXM0oyzBq~ literal 0 HcmV?d00001 diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java index b9173e7..401623b 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/common/Constants.java @@ -9,11 +9,15 @@ public interface Constants { public static final String TOKEN_PREFIX = "Bearer "; public static final String CURRENT_ROLE_CODE = "currentRoleCode"; public static final String COOKIE_TOKEN_CODE = "mall3_token"; + public static final String REDIS_USER_KEY = "ebtp:user_cache:"; + public static final String REDIS_USER_KEY_EXTEND = "ebtp:user_cache_extend"; + public static final String USERS = "users"; + public static final String ROLES = "roles"; public static final String TOKEN_EXPIRED = "90401"; public static final String REMOTE_ACCESS_FAILURE = "90500"; String ACTUATOR_HEALTH = "actuator/health"; String ACTUATOR_PROMETHEUS = "actuator/prometheus"; - String GET_USERINFO_API = "/v1/userinfo/get"; + String GET_USERINFO_API = "/v1/userinfo/refresh"; } diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java index fb9adce..b43e66b 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/security/starter/filter/TokenAuthenticationFilter.java @@ -1,11 +1,11 @@ package com.chinaunicom.mall.ebtp.cloud.security.starter.filter; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.exceptions.ExceptionUtil; import com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.AuthAllows; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.RoleCodeAuthority; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.SecurityUser; -import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.client.EbtpUserInfoClient; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.service.UserInfoService; import com.chinaunicom.mall.ebtp.common.base.entity.BaseCacheUser; import lombok.extern.slf4j.Slf4j; @@ -40,8 +40,6 @@ import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants. public class TokenAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserInfoService client; - @Autowired - private EbtpUserInfoClient ebtpClient; @Autowired @@ -101,6 +99,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { } catch (Exception e) { request.getSession().setAttribute("code", e.getMessage()); + ExceptionUtil.stacktraceToString(e); log.error(e.getMessage()); } filterChain.doFilter(request, response); @@ -140,8 +139,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { */ private Authentication getAuthentication(final String token, final String currentRoleCode, final boolean isWhite) { -// BaseCacheUser userInfo = client.getUserInfo(token); - BaseCacheUser userInfo = ebtpClient.get(); + BaseCacheUser userInfo = client.getUserInfo(token); +// BaseCacheUser userInfo = ebtpClient.get(); log.debug("getUserInfo:{}", userInfo.toString()); // 对象为空, 则说明网络异常feign已熔断 if (Objects.isNull(userInfo)) { diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/client/EbtpUserInfoClient.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/client/EbtpUserInfoClient.java index b65f4d7..267630d 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/client/EbtpUserInfoClient.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/client/EbtpUserInfoClient.java @@ -1,9 +1,9 @@ package com.chinaunicom.mall.ebtp.cloud.userinfo.starter.client; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.fallback.EbtpUserInfoClientFallbackFactory; -import com.chinaunicom.mall.ebtp.common.base.entity.BaseCacheUser; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; /** * 文档中心数据服务客户端 @@ -14,7 +14,11 @@ import org.springframework.web.bind.annotation.GetMapping; fallbackFactory = EbtpUserInfoClientFallbackFactory.class) public interface EbtpUserInfoClient { - @GetMapping("/v1/userinfo/get") - BaseCacheUser get(); - + /** + * 刷新redis缓存的信息 + * + * @return + */ + @PostMapping("/v1/userinfo/refresh") + public ResponseEntity refreshToken(); } diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheRole.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheRole.java new file mode 100644 index 0000000..5cad75b --- /dev/null +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheRole.java @@ -0,0 +1,46 @@ +package com.chinaunicom.mall.ebtp.cloud.userinfo.starter.entity; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 功能模块bean + * + * @author zyx + * + */ +@Data +@Accessors(chain = true) +@ApiModel +public class CacheRole implements Serializable { + + private static final Long serialVersionUID = 1L; + + /** + * 姓名 + */ + @ApiModelProperty(value = "角色") + private String role; + + /** + * 账号 + */ + @ApiModelProperty(value = "备注") + private String remarks; + + /** + * role_id + */ + @ApiModelProperty(value = "role_id") + private String roleId; + + /** + * 账号 + */ + @ApiModelProperty(value = "状态,0-默认") + private int status; +} diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheUser.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheUser.java new file mode 100644 index 0000000..399a100 --- /dev/null +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/entity/CacheUser.java @@ -0,0 +1,63 @@ +package com.chinaunicom.mall.ebtp.cloud.userinfo.starter.entity; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 功能模块bean + * + * @author zyx + * + */ +@Data +@Accessors(chain = true) +@ApiModel +public class CacheUser implements Serializable { + + private static final Long serialVersionUID = 1L; + /** + * id + */ + @ApiModelProperty(value = "编号") + private String id; + + /** + * 姓名 + */ + @ApiModelProperty(value = "姓名") + private String name; + + /** + * 账号 + */ + @ApiModelProperty(value = "账号") + private String account; + + /** + * 密码 + */ + @ApiModelProperty(value = "密码") + private String password; + + /** + * 省份 + */ + @ApiModelProperty(value = "省份") + private String province; + + /** + * 角色 + */ + @ApiModelProperty(value = "角色") + private String role; + + /** + * 租户 + */ + @ApiModelProperty(value = "租户") + private String tenant; +} diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/fallback/EbtpUserInfoClientFallbackFactory.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/fallback/EbtpUserInfoClientFallbackFactory.java index 39734ab..9d7ab3d 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/fallback/EbtpUserInfoClientFallbackFactory.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/fallback/EbtpUserInfoClientFallbackFactory.java @@ -3,6 +3,7 @@ package com.chinaunicom.mall.ebtp.cloud.userinfo.starter.fallback; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.client.EbtpUserInfoClient; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @Slf4j @@ -12,6 +13,6 @@ public class EbtpUserInfoClientFallbackFactory implements FallbackFactory null; + return () -> ResponseEntity.ok(false); } } diff --git a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/service/impl/UserInfoServiceImpl.java b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/service/impl/UserInfoServiceImpl.java index 0a1d0df..c0739b6 100644 --- a/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/service/impl/UserInfoServiceImpl.java +++ b/uboot-common/src/main/java/com/chinaunicom/mall/ebtp/cloud/userinfo/starter/service/impl/UserInfoServiceImpl.java @@ -3,23 +3,26 @@ package com.chinaunicom.mall.ebtp.cloud.userinfo.starter.service.impl; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.AuthorityEntity; import com.chinaunicom.mall.ebtp.cloud.security.starter.entity.SecurityEntity; +import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.client.EbtpUserInfoClient; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.client.UnifastOAuthClient; +import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.entity.CacheRole; +import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.entity.CacheUser; import com.chinaunicom.mall.ebtp.cloud.userinfo.starter.service.UserInfoService; import com.chinaunicom.mall.ebtp.common.base.entity.BaseCacheUser; +import com.chinaunicom.mall.ebtp.common.util.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.ResponseEntity; import org.springframework.remoting.RemoteTimeoutException; -import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.REMOTE_ACCESS_FAILURE; -import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.TOKEN_EXPIRED; +import static com.chinaunicom.mall.ebtp.cloud.security.starter.common.Constants.*; @Slf4j @Service @@ -29,9 +32,83 @@ public class UserInfoServiceImpl implements UserInfoService { UnifastOAuthClient client; // private @Autowired ObjectMapper userInfoObjectMapper; + @Autowired(required = false) + @Qualifier("cacheRedisTemplate") + private RedisTemplate redisTemplate; + + @Autowired + private EbtpUserInfoClient ebtpClient; + @Override public BaseCacheUser getUserInfo(String token) { - return convertToBusinessModel(getAuthentication(token.replaceAll("Bearer ", ""))); + token = token.replaceAll(TOKEN_PREFIX, ""); + + Object o = redisTemplate.opsForValue().get(REDIS_USER_KEY + token); + if (o == null) { + return setLocalUserCache(token); + } + return (BaseCacheUser) o; + } + + /* + * 本地缓存token + */ + private BaseCacheUser setLocalUserCache(String token) { + + BaseCacheUser user = structureUser(token); + + redisTemplate.opsForValue().set(REDIS_USER_KEY + token, user, 30, TimeUnit.MINUTES); + + return user; + } + + /* + * 构建用户实体,查询extend缓存或服务覆盖用户信息。 + */ + private BaseCacheUser structureUser(String token) { + //查询山分用户信息 + BaseCacheUser user = convertToBusinessModel(getAuthentication(token)); + + //获取redis中extend的缓存 + Object extendRedis = redisTemplate.opsForValue().get(REDIS_USER_KEY_EXTEND); + if (extendRedis == null) { + //查extend库 刷新缓存 + ResponseEntity response = ebtpClient.refreshToken(); + if (response.getStatusCode().value() != 200 || Boolean.FALSE.equals(response.getBody())) { + log.info("获取extend服务的user缓存失败"); + } + extendRedis = redisTemplate.opsForValue().get(REDIS_USER_KEY_EXTEND); + } + + if (extendRedis == null) { + return user; + } + + Map userTable = JsonUtils.jsonToPojo(String.valueOf(extendRedis), Map.class); + //获取用户信息 + List users = JsonUtils.jsonToList(userTable.get(USERS), CacheUser.class); + CacheUser baseUser = users.stream().filter(u -> u.getAccount().equals(user.getLoginName())).findFirst().orElse(null); + if (Objects.isNull(baseUser)) { + return user; + } + + List roles = JsonUtils.jsonToList(userTable.get(ROLES), CacheRole.class); + user.setProvince(baseUser.getProvince()) + //覆盖角色 + .setAuthorityList( + Optional.of(roles + .stream() + .filter(r -> baseUser.getRole().contains(r.getRole())) + .map(br -> + new AuthorityEntity() + .setRoleId(br.getRoleId()) + .setRoleCode(br.getRole()) + .setRoleName(br.getRemarks()) + .setRoleScope("EBTP") + .setAuthorities(Collections.emptyList())) + .collect(Collectors.toList())) + .orElseGet(Collections::emptyList)); + return user; } /** @@ -41,7 +118,7 @@ public class UserInfoServiceImpl implements UserInfoService { * @return */ private BaseCacheUser convertToBusinessModel(SecurityEntity raw) { - log.debug("userinfo: {}", raw); + log.info("userinfo: {}", raw); // 对象为空, 则说明网络异常feign已熔断 if (Objects.isNull(raw)) { throw new RemoteTimeoutException(REMOTE_ACCESS_FAILURE); @@ -94,6 +171,8 @@ public class UserInfoServiceImpl implements UserInfoService { } /** + * 获取用户信息 + * * @param token * @return */ @@ -115,7 +194,7 @@ public class UserInfoServiceImpl implements UserInfoService { */ private List filterByEBTP(List list) { return Optional.ofNullable(list).map( - ls -> list.stream().filter(auth -> "EBTP".equals(auth.getRoleScope())).collect(Collectors.toList())) + ls -> list.stream().filter(auth -> "EBTP".equals(auth.getRoleScope())).collect(Collectors.toList())) .orElseGet(() -> list); }