บทความนี้แนะนำวิธีการของ Spring-Boot รวมกับ Shrio เพื่อใช้ JWT และแบ่งปันกับคุณดังนี้:
เกี่ยวกับการตรวจสอบมีสองด้าน:
ทางออกหลัก: ใช้ตัวกรอง Shiro ที่กำหนดเอง
การก่อสร้างโครงการ:
นี่คือโครงการเว็บฤดูใบไม้ผลิ หากคุณไม่ทราบเกี่ยวกับการก่อสร้างโครงการ Spring-Boot Project โปรด Google
pom.mx แนะนำแพ็คเกจขวดที่เกี่ยวข้อง
<!-การจัดการการอนุญาต Shiro-> <การพึ่งพา> <roupId> org.apache.shiro </groupId> <ratifactid> Shiro-spring </artifactid> <sersion> $ {shiro.version} </เวอร์ชัน> </การพึ่งพาอาศัย <cersion> $ {shiro.version} </version> </การพึ่งพา> <!-jwt-> <cendency> <sderctiD> <roupid> io.jsonwebtoken </groupId> การกำหนดค่าที่เกี่ยวข้องกับ Shrio
ทำให้ประเด็น! - ปรับแต่งตัวกรอง
filtermap.put ("jwtfilter", jwtfilter ใหม่ ()); @ConfigurationPublic คลาส shiroconfig {@bean สาธารณะ ShirofilterFactoryBean getShirofilterFactoryBean (SecurityManager SecurityManager) {ShirofilterFactoryBean ShirofilterToryBean = New ShirofilterFactoryBean (); ShirofilterFactoryBean.SetSecurityManager (SecurityManager); // เพิ่มตัวกรองของคุณเองและตั้งชื่อมัน jwtfilter map <สตริงตัวกรอง> filtermap = new hashmap <> (); filtermap.put ("jwtfilter", jwtfilter ใหม่ ()); ShirofilterFactoryBean.setFilters (Filtermap); / * * กฎ URL ที่กำหนดเอง * http://shiro.apache.org/web.html#urls- */map <String, String> filterChainDefinitionMap = shirofilterFactoryBean.getFilterChainDefinitionMap (); FilterChainDefinitionMap.put ("/**", "JWTFilter"); ShirofilterFactoryBean.SetFilterChainDefinitionMap (FilterChainDefinitionMap); ส่งคืน ShirofilterFactoryBean; }/** * SecurityManager ไม่จำเป็นต้องฉีด ShirodBrealm โดยตรงซึ่งอาจทำให้เกิดการทำธุรกรรมล้มเหลว * สำหรับการแก้ปัญหาดู handleContextrefresh * http://www.debugrun.com/a/nks9ejq.html */@bean ( defaultWebsecurityManager manager = ใหม่ defaultWebSecurityManager (); manager.setRealm (tokenrealm); / * * ปิดเซสชันที่มาพร้อมกับ Shiro ดูเอกสารสำหรับรายละเอียด * http://shiro.apache.org/session-management.html#sessionmanagement-stateless-stateless%28Sessionless%29 */ค่าเริ่มต้น defaultSessionStorAgeEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator (); DefaultSessionStorageEvaluator.setSessionStorageEnabled (เท็จ); subjectDao.SetSessionStorageEvaluator (defaultSessionStorageEvaluator); manager.setsubjectdao (subjectdao); ผู้จัดการคืน; } @Bean Public LifeCycleBeanPostProcessor LifecyclecyBeanPostProcessor () {ส่งคืน LifecycleBeanPostProcessor ใหม่ (); } @Bean (name = "TokenRealm") @Dependson ("LifecycleBeanPostProcessor") สาธารณะ Tokenrealm Tokenrealm () {กลับ tokenrealm ใหม่ (); } @Bean @Dependson ("LifecycleBeanPostProcessor") Public DefaultAdvisorautoproxycreator defaultAdvisorautoproxycreator () {defaultAdvisorautoproxycreator defaultAdvisorautoproxycreator = new DefaultAdvisorautoproxycreator // บังคับ CGLIB เพื่อป้องกันข้อผิดพลาดพร็อกซีที่ซ้ำกันและพร็อกซีที่เป็นไปได้ // https://zhuanlan.zhihu.com/p/29161098 DefaultAdvisorautoproxycreator.setProxytargetClass (จริง); ส่งคืน defaultAdvisorautoproxycreator; } @Bean Public AuthorizationAtTributesourceAdVisor GetAuthorizationAtTributesourceAdVisor (SecurityManager SecurityManager) {AuthorizationAttributesourceAdvisor AuthorizationAttributesourceAdVisor = ใหม่การอนุญาตใหม่ AuthorizationAttributesourceAdvisor.SetSecurityManager (SecurityManager); ส่งคืน AuthorizationAttributesourceAdvisor (); - ปรับแต่งตัวกรอง Shrio
ลำดับการดำเนินการ: prehandle -> dofilterinternal -> executelogin -> onloginsuccess
คำพิพากษาหลักคือว่าคำขอเข้าสู่ระบบเป็น dofilterinternal หรือไม่
ระดับสาธารณะ JWTFilter ขยาย basichttpauthenticationFilter { / *** ปรับแต่งวิธีการดำเนินการเข้าสู่ระบบ* / @Override protected boolean executelogin (คำขอ servletRequest, การตอบสนอง servletResponse) USERNAMEPASSWORDTOKEN USERNAMEPASSWORDTOKEN = JSON.PARSEOBJECT (HTTPSERVLETREQUEST.GETINPUTSTREAM (), USERNAMEPASSWORDTOKEN.CLASS); // ส่งไปยัง Realm เพื่อเข้าสู่ระบบ หากข้อผิดพลาดนั้นไม่ถูกต้องมันจะโยนข้อยกเว้นและถูกจับหัวเรื่อง = this.getSubject (คำขอการตอบกลับ); subject.login (USERNAMEPASSWORDTOKEN); ส่งคืนสิ่งนี้ // ข้อผิดพลาดโยนข้อยกเว้น}/ *** วิธีแรกในการดำเนินการ*/ @Override prefected boolean prehandle (คำขอ ServletRequest, การตอบสนอง servletResponse) โยนข้อยกเว้น {return super.prehandle (คำขอ, การตอบสนอง); } / *** การดำเนินการเข้าสู่ระบบหลังจากเข้าสู่ระบบสำเร็จ* เพิ่มส่วนหัวของ JWT* / @Override Boolean onloginsuccess (โทเค็น AuthenticationToken, หัวเรื่องหัวเรื่อง, คำขอ ServletRequest, การตอบสนอง servletResponse) String jwttoken = jwts.builder () .setId (token.getPrincipal (). toString ()) .Setexpiration (datetime.now (). บวก (30) .Todate ()). httpservletResponse.addheader (Authorization_header, Jwttoken); กลับมาจริง; } / *** กระบวนการหลักของการเข้าสู่ระบบและการตรวจสอบ* พิจารณาว่าเป็นการเข้าสู่ระบบหรือคำขอสามัญหลังจากเข้าสู่ระบบ* / @Override โมฆะสาธารณะ dofilterInternal (servletRequest servletrequest, servletexceptions servletexception (httpservletrequest) ServletRequest; httpservletResponse httpservletResponse = (httpservletResponse) servletResponse; String servletPath = httpservletRequest.getServletPath (); if (stringUtils.equals (servletpath, "/เข้าสู่ระบบ")) {// executelogin (servletRequest, servletResponse); } else {String AuthenticationHeader = httpservletRequest.getheader (Authorization_header); if (stringutils.isnotEmpty (AuthenticationHeader)) {อ้างสิทธิ์ในตัว = jwts.parser () .setsigningKey (jwtcost.signaturekey) .parseclaimsjws (Authenticationheader) .getbody (); if (body! = null) {// update token body.setexpiration (datetime.now (). plusminutes (30) .todate ()); String updateToken = jwts.builder (). setclaims (body) .compact (); httpservletResponse.addheader (Authorization_header, updateToken); // เพิ่มข้อมูลรับรองผู้ใช้หลัก principalcollection principals = ใหม่ SimplePlincipalcollection (body.getId (), jwtcost.usernamepasswordrealm); // รวบรวมข้อมูลผู้ใช้ shiro websubject.builder builder = ใหม่ websubject.builder (ServletRequest, ServletResponse); builder.principals (หลักการ); builder.authenticated (จริง); builder.sessionCreationEnabled (เท็จ); webSubject subject = builder.buildWebSubject (); // ใส่ลงในคอนเทนเนอร์และโทร threadcontext.bind (หัวเรื่อง); FilterChain.dofilter (httpservletRequest, httpservletResponse); }} else {httpservletResponse.setStatus (httpstatus.forbidden.value ()); - การประมวลผลเข้าสู่ระบบล้มเหลว
จัดการข้อยกเว้น Shrio
@RestControllerAdvicePublic คลาส GlobalControllerexceptionHandler {@ExceptionHandler (value = exception.class) วัตถุสาธารณะ AllexceptionHandler (httpservletRequest Request, httpservletResponse การตอบสนองยกเว้นข้อยกเว้น) logutil.error (ข้อความ); ส่งคืนผลลัพธ์ใหม่ Info (Exception.getClass (). getName (), ข้อความ); } /*=========== การสกัดกั้นข้อยกเว้นของ Shiro ===============================* / @ExceptionHandler (value = ไม่ถูกต้อง Response.setStatus (httpstatus.forbidden.value ()); return "ไม่ถูกต้อง credentialsexception"; } @ExceptionHandler (value = unknownAccountexception.class) สตริงสาธารณะ unknownaccountexception (คำขอ httpservletrequest, httpservletResponse การตอบสนอง, ข้อยกเว้นข้อยกเว้น) {response.setStatus กลับ "unknownaccountexception"; } @ExceptionHandler (value = lockedAccountexception.class) สตริงสาธารณะ LockedAccountexception (คำขอ httpservletRequest, การตอบสนอง httpservletResponse, ข้อยกเว้นข้อยกเว้น) {repections.setStatus (httpstatus.forbidden.value (); กลับ "LockedAccountexception"; } @ExceptionHandler (value = expoliveAtEmpSexception.class) สตริงสาธารณะส่วนเกิน epureTextEmpSexception (คำขอ httpservletrequest, httpservletResponse การตอบสนอง, ข้อยกเว้นข้อยกเว้น) {setSetStatus (httpstatus.forbidden.value ()); คืน "oppleatEmtSextsexception"; } @ExceptionHandler (value = authenticationException.class) การรับรองความถูกต้องของสตริงสาธารณะ (คำขอ httpservletRequest, การตอบสนอง httpservletResponse, ข้อยกเว้นข้อยกเว้น) {response.setStatus (httpstatus.forbidden.value (); กลับ "AuthenticationException"; } @ExceptionHandler (value = unauthorizedException.class) สตริงสาธารณะที่ไม่ได้รับอนุญาต Exception (คำขอ httpservletRequest, การตอบสนอง httpservletResponse, ข้อยกเว้นข้อยกเว้น) {setSetStatus (httpstatus.forbidden.value (); กลับ "unauidizedexception"; -การจัดการข้อยกเว้น JWT
นี่เป็นข้อผิดพลาดเพราะเป็นข้อยกเว้นที่เกิดขึ้นในตัวกรองและ @ExceptionHandler ไม่สามารถสกัดกั้นได้
/*** สปริงข้อผิดพลาดในการบูตสปริง*/ @restControllerPublic คลาส globalexceptionhandler ใช้ errorController {@Override สตริงสาธารณะ getERRORPATH () {return "/ข้อผิดพลาด"; } @RequestMapping (value = "/ข้อผิดพลาด") ข้อผิดพลาดวัตถุสาธารณะ (คำขอ httpservletRequest, การตอบกลับ httpservletResponse) โยนข้อยกเว้น {// การจัดการข้อผิดพลาดข้อยกเว้นลอจิกยกเว้น (ข้อยกเว้น) คำขอ. สาเหตุที่โยนได้ = Exception.gatecause (); if (สาเหตุของอินสแตนซ์ของ ExpiredJwtexception) {response.setStatus (httpstatus.gateway_timeout.value ()); ส่งคืนผลลัพธ์ใหม่ ๆ ("Expiredjwtexception", cause.getMessage ()); } if (สาเหตุของอินสแตนซ์ของ malformedjwtexception) {response.setStatus (httpstatus.forbidden.value ()); ส่งคืนผลลัพธ์ใหม่ ๆ ("malformedjwtexception", cause.getMessage ()); } ส่งคืนผลลัพธ์ใหม่ ๆ (cause.getcause (). getMessage (), cause.getMessage ()); -เกี่ยวกับข้อมูลการอนุญาตเช่นการอนุญาตคุณสามารถนำไปใช้โดยตรงใน Redis เพื่อแคช ฉันคิดว่ามันดีเหมือนกัน
แหล่งที่มาที่นำเสนอ: Githup-Shiro Branch: การเตือนความจำที่อบอุ่น: รหัสทดสอบอาจยุ่งในชีวิตประจำวัน
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น