ملخص
كما نعلم جميعًا ، باستخدام JWT للتحقق من الإذن. بالمقارنة مع الجلسة ، فإن ميزة الجلسة هي أن الجلسة تتطلب كمية كبيرة من ذاكرة الخادم ، وعندما يتم استخدام خوادم متعددة ، ستشمل مشكلات جلسة مشتركة ، والتي تكون أكثر إزعاجًا عند الوصول إلى محطات المحمول مثل الهواتف المحمولة.
لا تحتاج JWT إلى تخزينها على الخادم ولا تشغل موارد الخادم (أي ، عديمة الجنسية). بعد قيام المستخدم بتسجيل الدخول ، يعلق على الرمز المميز عند الوصول إلى الطلب الذي يتطلب إذنًا (عادةً ما يتم تعيينه في رأس طلب HTTP). لا تعاني JWT من مشكلة مشاركة خوادم متعددة ، كما أنه لا يملك مشاكل في الوصول إلى الهاتف المحمول على الهواتف المحمولة. إذا كان من أجل تحسين الأمان ، يمكن أن يكون الرمز المميز مرتبطًا بعنوان IP الخاص بالمستخدم
عملية الواجهة الأمامية
قام المستخدم بتسجيل الدخول عبر Ajax للحصول على رمز رمزي
بعد ذلك ، عند الوصول يتطلب إذنًا ، قم بتوصيل رمز مميز للوصول.
<! doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <title> العنوان </title> <script src = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js "" ؛ دالة تسجيل الدخول () {$ .post ("http: // localhost: 8080/auth/login" ، {username: $ ("#username"). val () ، كلمة المرور: $ ( "GET" ، url: } </script> </head> <body> <fieldset> <legend> الرجاء تسجيل الدخول </legend> <sable> اسم المستخدم </label> <input type = "text" id = "username"> <blabel> كلمة المرور </label> <input type = "text" id = "password" id = "touserPageBtn" onClick = "TouserPageBtn ()"> Access Userpage </utton> </body> </html>عملية الواجهة الخلفية (SPRING BOOT + Spring Security + JJWT)
الأفكار:
اكتب فئة كيان المستخدم وأدخل جزءًا من البيانات
فئة كيان المستخدم (المستخدم)
@data @entitypublic class user {idgeneratedValue private int id ؛ اسم السلسلة الخاصة ؛ كلمة مرور السلسلة الخاصة ؛ manytomany (cascade = {cascadetype.refresh} ، petch = petchtype.eager) jointable (name = "user_role" ، "ID")}) القائمة الخاصة <BOON> الأدوار ؛} دور (إذن) فئة الكيان
@data @entitypublic class or {idgeneratedValue private int id ؛ اسم السلسلة الخاصة ؛ manytomany (medbyby = "ROLES") قائمة خاصة <Sether> المستخدمين ؛} أدخل البيانات
جدول المستخدم
| بطاقة تعريف | اسم | كلمة المرور |
|---|---|---|
| 1 | لينوا | 123 |
جدول الدور
| بطاقة تعريف | اسم |
|---|---|
| 1 | مستخدم |
جدول user_role
| uid | يتخلص |
|---|---|
| 1 | 1 |
واجهة طبقة DAO ، تحصل على البيانات من خلال اسم المستخدم ، وإرجاع كائن اختياري بقيمة Java8
الواجهة العامة userRepository تمتد مستودع <المستخدم ، integer> {اختياري <Sether> findByName (اسم السلسلة) ؛} اكتب logindto لنقل البيانات مع الواجهة الأمامية
datapublic class logindto تنفذ serializable {notblank (message = "اسم المستخدم لا يمكن أن يكون فارغًا") اسم المستخدم الخاص ؛ notblank (message = "كلمة المرور لا يمكن أن تكون فارغة") كلمة مرور السلسلة الخاصة ؛} اكتب أداة توليد رمزية وقم بإنشائها باستخدام مكتبة JJWT. هناك ثلاث طرق في المجموع: قم بإنشاء رمز (إرجاع سلسلة) ، وتحليل الرمز المميز (إرجاع كائن مصادقة المصادقة) ، والتحقق من الرمز المميز (إرجاع قيمة منطقية)
componentpublic class jwttokenUtils {private final logger log = loggerfactory.getLogger (jwttokenutils.class) ؛ private static final string electisties_key = "auth" ؛ سلسلة خاصة. // التوقيع على مفتاح خاص طويل الرمز الطويل // تاريخ انتهاء الصلاحية الخاصة الطويلة الطويلة tokenvalidinMillisEcondSforremempme ؛ // (تذكرني) تاريخ انتهاء الصلاحية postconstruct public void init () {this.secretkey = "linyuanmima" ؛ int secondin1day = 1000 * 60 * 60 * 24 ؛ this.tokenValityInmillisEconds = SecondIn1day * 2l ؛ this.tokenValidinMillisEcondSforRemempme = SecondIn1day * 7l ؛ } وقت انتهاء الصلاحية الطويل الطويل = 432_000_000 ؛ // إنشاء Token Public String CreateToken (مصادقة المصادقة ، remlearme boolean) {string exectities = antaintication.getAuthorities (). Stream () // الحصول على سلسلة إذن من المستخدم ، مثل المستخدم ، المسؤول .map. طويل الآن = (تاريخ جديد ()). getTime () ؛ // الحصول على صحة تاريخ الطابع الزمني الحالي ؛ // وقت انتهاء التخزين إذا (تذكر) {صحة = تاريخ جديد (الآن + هذا. } آخر {صحة = تاريخ جديد (الآن + هذا. } return jwts.builder () // إنشاء رمز رمزي. } // الحصول على أذونات المستخدم المصادقة العامة getAuthentication (string token) {system.out.println ("token:"+token) ؛ مطالبات المطالبات = jwts.parser () // parse token's payload .SetSigningKey (secretKey) .Parseclaimsjws (token) .getBody () ؛ مجموعة <؟ يمتد GrantedAuthority> السلطات = المصفوفات. // تحويل العناصر إلى مجموعة مستخدم مجموعة واجهة GrantedAuthority = مستخدم جديد (PlayS.getSubject () ، "" ، السلطات) ؛ إرجاع جديد usernamepasswordauthenticationToken (الرئيسي ، "" ، السلطات) ؛ }. // التحقق من الرمز المميز عن طريق العودة المفتاح. } catch (signatureException e) {// signature issection log.info ("invalid jwt signature.") ؛ log.trace ("تتبع توقيع JWT غير صالح: {}" ، e) ؛ } catch (malformedjwtexception e) {// jwt format error log.info ("invalid jwt token.") ؛ log.trace ("تتبع رمز JWT غير صالح: {}" ، e) ؛ } catch (ExpiredJwTexception e) {// jwt منتهي الصلاحية log.info ("منتهية الصلاحية jwt token.") ؛ log.trace ("منتهية الصلاحية JWT TOKEN TRACE: {}" ، e) ؛ } catch (UnsupportedJwTexception e) {// the jwt log.info ("unusted jwt token.") ؛ log.trace ("Trace jwt jwt غير المدعوم: {}" ، e) ؛ } catch (alfulalArgumentException e) {// the parmeter rerric issection log.info ("jwt token compact of handler غير صالح.") ؛ log.trace ("JWT Token Compact of Handler هي تتبع غير صالح: {}" ، e) ؛ } إرجاع خطأ ؛ }} تنفيذ واجهة userDetails ، التي تمثل فئة كيان المستخدم ، يتم لفها على كائن المستخدم الخاص بنا ، ويحتوي على أذونات وخصائص أخرى ، ويمكن استخدامها بواسطة Spring Security
الفئة العامة myUserDetails تنفذ userDetails {user user user ؛ public myUserDetails (مستخدم المستخدم) {this.user = user ؛ } Override Public Collection <؟ يمتد GrantedAuthority> getAuthorities () {list <row> roles = user.getRoles () ؛ قائمة <UrnessedAuthority> السلطات = ArrayList جديد <> () ؛ StringBuilder sb = new StringBuilder () ؛ if (roles.size ()> = 1) {for (دور الدور: الأدوار) {exectorities.add (جديد simplegrantedauthority (rob.getName ())) ؛ } إرجاع السلطات ؛ } إرجاع السلطات ؛ } return AuthoritutIls.CommaseParatedStringToAuthorityList ("") ؛ } Override public string getPassword () {return user.getPassword () ؛ } Override public string getUserName () {return user.getName () ؛ } Override public boolean isaccountnonexpired () {return true ؛ } Override public boolean isAccountNonlocked () {return true ؛ } Override public boolean iscredentialsnonexpired () {return true ؛ } Override Public Boolean Isenabled () {return true ؛ }} قم بتنفيذ واجهة userDetailsService ، والتي لديها طريقة واحدة فقط للحصول على userDetails. يمكننا الحصول على كائن المستخدم من قاعدة البيانات ، ثم نلفه في userDetails وإعادته
servicepublic class myUserDetailsService تنفذ userDetailsService {autowired userrepository userrepository ؛ Override public userDetails loadUserByUserName (سلسلة S) يلقي usernamenotfoundException {// تحميل كائن المستخدم من قاعدة البيانات اختيارية <Sether> user = userrepository.findbyname (s) ؛ // للتصحيح ، في حالة وجود القيمة ، يتم إخراج اسم المستخدم وكلمة المرور. ifpresent ((value)-> system.out.println ("username:"+value.getName ()+"كلمة مرور المستخدم:"+value.getPassword ())) ؛ // إذا لم تعد القيمة ، فإن إرجاع null return new myuserDetails (user.orelse (null)) ؛ }} اكتب مرشحًا. إذا كان المستخدم يحمل الرمز المميز ، فسوف يحصل على الرمز المميز ، ويقوم بإنشاء كائن مصادقة مصادقة استنادًا إلى الرمز المميز ، وتخزينه في SecurityContext للتحكم في الإذن بواسطة Spring Security.
الطبقة العامة jwtauthenticationTokenFilter يمتد genericfilterbean {private final logger log = loggerfactory.getLogger (jwtaUtenticationTokenFilter.class) ؛ @Autowired jwttokenutils tokenprovider ؛ Override public void dofilter (servletrequest servletrequest ، servletResponse servletResponse ، filterchain filterchain) يلقي iOexception ، servletexception {system.out.println ("jwtaUtenticationTokenFilter") ؛ جرب {httpservletrequest httpreq = (httpservletrequest) servletRequest ؛ String jwt = resolvetoken (httpreq) ؛ if (stringUtils.hastext (jwt) && this.tokenProvider.validatetoken (jwt)) {// تحقق مما إذا كان JWT مصادقة صحيحة = this.tokenprovider.getAuthentication (jwt) ؛ // احصل على معلومات مصادقة المستخدم SecurityContextholder.getContext (). setAuthentication (المصادقة) ؛ // حفظ المستخدم إلى SecurityContext} filterchain.dofilter (servletRequest ، servletResponse) ؛ } catch (ExpiredJwTexception e) {// jwt invalid log.info ("استثناء الأمان للمستخدم {} - {}" ، e.getClaims (). getUbject () ، e.getMessage ()) ؛ log.trace ("TRACE Security Exception Trace: {}" ، e) ؛ ((httpservletresponse) servletResponse) .SetStatus (httpservletresponse.sc_unauthorized) ؛ }} سلسلة خاصة Resolvetoken (طلب httpservletrequest) {String BearerToken = request.getheader (WebSecurityConfig.Authorization_header) ؛ // احصل على رمز من رأس http if (stringUtils.hastext (bearertoken) && bearertoken.startswith ("Bearer")) {return bearertoken.substring (7 ، bearertoken.length ()) ؛ // إرجاع سلسلة الرمز المميز وإزالة السلسلة jwt = request.getParameter (WebSecurityConfig.authorization_token) ؛ // احصل على رمز رمز من معلمات الطلب if (stringUtils.hastext (jwt)) {return jwt ؛ } إرجاع فارغ ؛ }} اكتب logincontroller. يقوم المستخدم بالوصول /AUTH /تسجيل الدخول من خلال اسم المستخدم وكلمة المرور ، ويستقبله من خلال كائن Logindto ، ويقوم بإنشاء كائن مصادقة. الكود هو usernamepasswordauthenticationToken لتحديد ما إذا كان الكائن موجود. تحقق من كائن المصادقة من خلال طريقة المصادقة المصادقة. سوف تتحقق فئة تنفيذ AuthenticationManager ProviderManager من خلال AuthenticationProvider (معالجة المصادقة). يدعو Providermanager الافتراضي daoauthenticationprovider لمعالجة المصادقة. ستحصل DaoAuthenticationProvider على استخدام userDetails من خلال userDetailsService (مصدر معلومات المصادقة) ، إذا نجحت المصادقة ، يتم إرجاع الأذونات التي تحتوي على أذونات ، ثم ضبطها على SecurityContext من خلال SecurityContextholder.getContext ().
RestControllerPublic Class LoginController {Auutowired userrepository userrepository ؛ @AUTOWIRED InitienticationManager AuthenticationManager ؛ @autowired jwttokenutils jwttokenutils ؛ requestmapping (value = "/up/login" ، method = requestMethod.post) تسجيل الدخول العام (valid logindto logindto ، httpservletresponse httprespons usernamepasswordauthenticationToken (logindto.getusername () ، logindto.getPassword ()) ؛ // إذا كان كائن المصادقة غير فارغ إذا (Objects.nonnull (antainicationToken)) {userrepository.findbyName (antasticationtoken.getPrincipal (). } جرب {// التحقق من كائن المصادقة من خلال مصادقة anityManager (تم تنفيذها الافتراضي على أنه providermanager) المصادقة = المصادقة manager. // BIND المصادقة لـ SecurityContext SecurityContextholder.getContext (). setAuthentication (المصادقة) ؛ // إنشاء الرمز المميز لسلسلة الرمز المميز = jwttokenUtils.createToken (المصادقة ، خطأ) ؛ // اكتب الرمز المميز إلى HTTP Header HttPresponse.addHeader (WebSecurityConfig.authorization_header ، "Bearer"+Token) ؛ إرجاع "حامل"+رمز ؛ } catch (مصادقة badCredentialSexception) {رمي استثناء جديد ("خطأ كلمة المرور") ؛ }}} اكتب فئة تكوين الأمان ، ورث websecurityConfigurerAdapter ، وتجاوز طريقة التكوين
@configuration@enablewebsecurity@enableGlobalmethodSecurity (prepoStenabled = true) فئة عامة websecurityconfig يمتد websecurityConfigureRadapter {public static final elevization_header = "Eutlization" ؛ STATIC FINDING FINDINGERISTIONS_TOKEN = "Access_Token" ؛ AUTOWIRED userDetailsService userDetailsService ؛ Override محمية باطلة التكوين (AuthenticationManagerBuilder Auth) يلقي الاستثناء {Auth // تخصيص للحصول على معلومات المستخدم. UserDetailsService (UserDetailsService) // تعيين كلمة المرور charption.passwordencoder (passwordencoder ()) ؛ } override contected void تكوين (httpsecurity http) يلقي الاستثناء {// تكوين سياسة الوصول إلى طلب الوصول http // csrf و cors .cors (). disable () .csrf (). . و () // تحقق من طلب http.authorizerequests () // السماح لجميع المستخدمين بالوصول إلى الصفحة الرئيسية وتسجيل الدخول. . و () // set logout (). permanly () ؛ // إضافة مرشح JWT في http .addfilterbefore (genericfilterbean () ، usernamepasswordauthenticationfilter.class) ؛ } bean provorpordencoder passworderencoder () {return new BcryptPasswordEncoder () ؛ } bean public genericfilterbean genericfilterbean genericfilterbean () {return new jwtauthenticationTokenFilter () ؛ }} اكتب وحدة تحكم للاختبار
RestControllerPublic Class UserController {postmapping ("/login") public string login () {return "login" ؛ } getMapping ("/") public string index () {return "hello" ؛ } getMapping ("/userpage") السلسلة العامة httpapi () {system.out.println (securityContexTholder.getContext (). getAuthentication (). getPrincipal ()) ؛ إرجاع "صفحة المستخدم" ؛ } getMapping ("/adminPage") السلسلة العامة httpsuite () {return "userpage" ؛ }}تنزيل رمز مصدر الحالة (التنزيل المحلي)
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.