Authentication:身份认证/登录,验证用户是不是拥有相应的身份。
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。
这里我们主要分析Authentication过程
一般在登陆方法中我们会这么写:
Subject subject =SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
其中username,paasword为客户端登陆请求传过来的账号密码,现在我们只要知道 token中含有客户端输入的账号密码,那么怎么和数据库中的做验证呢?
再看spring中shiro的一部分xml配置:
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAxwAAABlCAYAAADHwuWcAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABvISURBVHhe7Z3PcuREtof9PNVs6L400MC9TUc4GNoREBB44DIR7t6waG8m2LiDVbPirhwBz+ANGx7As2AegAfpeQvdOqk6paP0yT9SSS6V/S2+qFL+OZnKTKXOL5VVOnr30XuNcnp62jx48AAAAAAAAGASEBwAAAAAADAbCA4AAAAAAJgNBAcAAAAAAMwGggMAAAAAAGYDwQEAAAAAALOB4AAAAAAAgNlAcAAAAAAAwGwgOAAAAAAAYDYQHAAAAAAAMBsIDgAAAAAAmA0EBwAAAAAAzAaCAwAAAAAAZgPBAQAAAAAAs3FnBMdq9bj59fdXzR8vV278UlitjpuLq+vm+uqiOV7l67o6u6xKp6w+fta8+OW75vRk1axOnjevfvmqOfl42e0Rs1o9ak5ef9e8ev2seVx53tAyd/8fX1w1//rXvwLXl2dumn1yF8b/XOh1JW3jxc9NbfmHev3vu32XhF6H5//3v4EX3z9y0w3l8fdfbW2+On/ippmLu9K/h3p9LQHuL7uD4LhlEBx5ShPi0/PvRt3AVqsnzem6bWxeuYHdpUnjtvpfhAeC47DYt8NUW/6hOkT7bl9l9PwYiYSxTr3Os3O2Q5i3E3VLnX8qPGfLUtO/msa24Zi+mJPS9cX9NQ33l905EpGRw8u0BFarp80ff75ofv2s7fBDERxDGCc42ovAfvfSHio73VBff7WmnWjbiVeO79qEOH//L1tw3O3xP5Z9O8T7Ln9ulnJ+uwmO583Tzb2m5JimiO3MwRjBkcozleDQ+DFtvyS4v6Yp3V9WL0+bt7+fNF/OOPYPnSMvUFmq4Fh9dtL89eer5q+fHndhW8HRfv7n3+fNWyNIlH/+1saFeGdw9OL/PG3+uYnv7IvQSdvPUbMl5ezyepsmpBsgOEropGhXYOIJVCYcjfNuNqLsbX6dnLwJOawCGBsyuUt6+Yzza7yGxzcBG2fRdO4EEE8Q67qcnHfHp98/CZOizdM7f+cGfHrSruS08f28ufzWhsYrts1y7V9qvykQkWvH39XF8Y00OcFhx+/19WVz1jv/9umetX95Vh+/K3H7x/1Tat+a/isx5/gSytdndw5F+871n8NrH22bmvJtu6ecwyH947VPCrHplSntYctItU9t+6YIc+X5en7a5NVxYG147dumXfXaxFLjTAvtXBnNV86cWhofnh1Lavx27WfKj+4fitdXpfNP9a+ET9G/tU92iu3H/fWg76/iP1q/EfocnOAQFSnOfvwkQwWBjQuK03R+GAy/Pd3miY+//OlFz26I34gStW9FhqS3+WtJOWzirNnwoU84SsiF5E26ilyMNj4+DhOcMwmEuMoJ0V7o3iSm6VL1lDp5k4CWH0+wvQlTyl3XqZ1Unq/L7ddZ0seT07auG/v2/ON65vJvj7U+Tn3j9o6PxX5N+40ljLfrq+biOG8vNX4l3AqEMJ7N+M0JFaEUvwvJ8WH6p9S+pf4rkRsfak/Kt+Mxrl9ufNVcnzfsZ8ZbfFwitmepKV/Jhaf6p6Z/c8RzlSJtoPXNtY93fm1/pJ1vi6TVvMFuOK/Wple+d76aZsiYVNq29B04tZc7f+2bGHv+kkbPZZtf+3JTlo1P9UlqfAip8w+2TF3tOdWcX03/SnobH5OzL7T28tdvrn20D7Ztmrg/jGo/Z7xZO6EsKXddJ0lzn++vwS8cuBh9XzgowZHrSG9Lld12FW/BCvHhSUlajdpHZK79kY/QPMdqtTprLiNnzwoOXUGRC8JiL7gS4QJO3ABb+/2LK74JyQXqTUZC7YRoj70yt+nMRGDJ1sGUF9eni1uX+fp5c3ou32/W2ZKzF8fbfF68l9+eZ03717bfWEQgeE80YmqFQSyYQ77oqYclF7/r+I/bMoRF7Zdr31L/jaE4vgr9G4+/Xa5PryyvzXKE9kjOL/XXT6pds/1T0b85Qv6NbSlH2tHWudQ+Y/rPYttCz19txnXRPF475cZADr/9bPl148MLS1Ec/wPHh5A6/87Wupzzdf9u7Er6KftXytnOTaaONe23y/Urx7nrQ/Ns0w1sPyHXX13cukzur8mF8ftOEBz6ew2LhtvE+8R7wuDFJwXHZhuWDAKLteeluTXBcXzRXMVbUCZ+wiHIReVOiOHicxy6zQXnXdCW6gkxMdFZRk+IZoII52MnD6nLxqbauDFpOm0wZELM5ddyuwmwtafnUmp/SVPbfmPQ7Uw1W5hSgqMdw/ktgZJ3G+fYKMWPxbt5xX1aat9c/9UweHxFN7yh+S2ufXvDrhh/NSTnl0L5Giak+iHXPzX9m6Nta3E+1nnOrcNk5pNM+9T0Xw5bfz1PtaljrGb85ebHHO359YWCLaN2fHh2+nEDxv/A8SGkzn9rS+qwzvt0s/VH0kuZU/dvG7fuLz2/Efb79srtk2sXy5j2E+z5hvMxZYe6bGyqjbjOu/Z/Lr+Wm7o+Su0vaWrbrwb1Jcf4h3eZO7elKik4nCccFs3f+13IbT7hcARHSDfhEw6LXpDdBZq/OcYXcCp+nxOioPFiw6azE6JF0kudvfOz9S+dXym/HEtZvb4z9Sm1vzDlhBijgmPsEw4vf04wa/qUqIjjdx3/7Q0ndqj6bV5q31z/lRg1vkyda/PbeItrv5e/PP6GoOVpG5XK1zAh1Q+5/qnp3xxt2nX+tZ3TdRu2DukmrKJ93PMbUr5pCz3PuE9rxp/Of3F4Cbf9Qlhb/9pz8eyE8OhcQlhp/A8cH0Lq/Lf1WtuUMsT2C/Mbgzn6d0j7ee3jxefapzR/KWPaT9F4sXGjLxPjMbT3jv1fyi/HUlbq+qgZv7XtV4ItVWmO3nvvPTdCwkVwvPPOO278Psn/aLzr5NJvNiytIOny6/GtCY7IYRNnzVshnpL4ApMLNnfBhfTOzUSx+dvJdn088YQY4ozNGJ3kX7zu1zNlUyfQdkLqJjw9rp8QS/nlON12QlX7V7TfWMK4zGx5UvzxK1sCuyckepwbv54dSyl+CN4NK7R35fis6T9h2+9R2vL4uDm+etdTIb+Ehfon6lgav3JcGn9C6vw8bHvWlK+k+iHfP+X+bdP59W/zf9WcroWG1FHq9uJ8TWX7lPqvS5co37SFnqc9pzZfZZs7baoky4+EwjadqX/V+IjsbMM39nLj19r37h9Kbhykzj++L7Tly3HnhE7Rv5a4LlXpM31cap9cu1jGtJ8St6OGp2xKne/T/TX4mPxoPMnR119/HcSFDZRjCf/000974UsifmKhgqC3HSoSF6U0+vQkhIvtl2thM5HgUEGh20W8bSN2S0pw1CbcUqUXdG8FIJpY3DTRBSgXpY23N3idBEM+ufjl2JSRu6C9soOdKH2cLhXv1dsrWydE+S4T3NaurIacdPVXu6kJsZRf4uO2C+lMnUrtP8WEWEKcfDs+twK4ZvxuRHIIl98jna3H82b8evnt2C7FT4HepLZtG43/UvuW+k/Y3gg9p6tifGVtF8aXENdxO7Zrxm9NHRLn5+Z1bKcdipv5gw1Tfql/Sv1r03j9ExySTfg2nSkv1z41bdemS7SfaQs9T7WpfVg3/vr1uBmfKN/M3YrtqzZv+RxbOzfbNsQVxm/u/uGVHdJVnv/2vCN7oR4bwZE7v9K5e+0Xj7+SDSF1/Yb8mfbRvLE9xSs72InSx+lS8V69vbLv0/117AL0feLoyUefBHGhokPFRixCAA4Bb+JaAu3Noru5hbDNTXBpdYWbzN1/Sx23sAyYP2AJcH+FXQhvGlfR8cknffEBcGiEVYpoZWkJhNWaeEJ0JklYJnP3310QHA8/fNT8/c0/wucSjw8Z5g9YAtxfYReC4BA++uS/w282Hj/ufhcBcCjo49R4q8CSiB/5xhMkLJs5++/QBceHnz0Jzv1SP706HxrMH7AvuL/CFGwFh7DEf6UCAIBlI0597ZOGfRzLJwAA7I+DExzvv/++Gw4AAAAAAMsDwQEAAAAAALOB4AAAAAAAgNlAcABsaP/Vov3hrvevF3PC36KW2Wf/WOz7coSat7MLu9S/dnxouiX+k0wOxn+ZoeNHXkJmX44LALBPEBzQ4z7fpA5ZcOh/jtsXRcm/duzLKZ+DJQiO+I3qQ7gLgkPqbf8Jxo63XWD8lxkyflafnTR/8cZjAFgQCI6RxG86vysgONqbuP3upZ2anR0uqe/rr6I3t8rxXXO49tM/Svt047I5G+HI7VL/XcfHFHgO/OPvn0/SB4z/MkPGz5c/vbgxj/MmZADYJwiOEbSrR33HfLV63Pz6+6vmj5ciRF41//n3efM2EiR6E5BPiRc8Gxr31qxQ1dgXRDBs80c3l1z5Nszy9renIb495355XliK1eq4ubiSleF2hVi2olxfXzUXx/28Z5fdVpVr49itzi6b68uLYEPi1E5so5f/6qI5nvjmWlrhfXr+3TbO+8/yVP7O4Wo/2/w3HYqefbOK3Tpcz5qT884hOf3+SXC6rI1U/bry25XiqvKj81MbGq9YJzJVf0EcWmkP+3/qU62gC8cXV2H7k3zqGIm3Q5XGzy6Co4TXftp2NePDttur8yfbcBufa9+4/P74qHtrL+O/i1due/znFsPk/mDvKwAAtwWCYyCySiSO+B8v+xO2FQsaJ068dfrVqdcw67BrfitAbP4a++FmshEI3nGufJvH1kFJ1s/Yz6GCwwoEcfyuL8+2aeTYblUR50+dPhEc4gRKfAgPdlTEtHlCuLEXH+9KaRuDOAqxc9FzijL5rbOiNsSedRyDvcSxOlyP12W0TousPKsT1dlL1U/Lt/WLyy+dX68+G3vWYcrVXxD71hEL55Rp76Go0NiOqSAeuvGYGz9WpFisKN6VuL0tNeNDyYWn2tfrr2Cnl/amgLBI+tz4YPx38d6x2J9i/Jfm5RBfuVAEADAVCI4B5Cbq7glEFxevNIX8RiDY+Nb576889eIL9r1VrdhmrnzNkxIcgn0k79UnR/eEw9RPnlpknkLYePtdhYralFXqdm99/2mHXY3WFVq5oVusw1BCHATrQJQIDlbkkKTyq4NiHRqbv61/3/mwTmCXdp3u9fPm9Fy+37Rp6dvPl2/zefFefuuwleq/TW/K8/LsQhg3ZrzZMVMaP7mwED7B+Arnn3Dqh/SPbfcb4Yn29QRFL17KKgiOmLh+jP/5x3/tvJxaOAMAmAsERyU6ke8sOBIrT97+WmuzZL8VF912KsXWN1e+khUccXlRfXPUCI7Wmeu2tIQV5FrB4eQN+SMnciyeQxHTOhB9pzPnkPTyFhwez3awbx3CjXOjjl1sc2j9Yocrl1/L7Rys1p46mKX6S5qUozwVOm68uNrxkxIcUyFtsG0b0xY1/aOk2jHXvp4tW2aILwgOxv/+x3/t7zT0flGTFgBgChAcAyltqeoJAu8JQ0pwRGlDmHXwC/a9pxUxuwoOQePFVi5dTElwWPHgxo94wmFpVwudG75xGHLEDkRNfM9hqsyfdLgKq53W4bKIEyQ2a+uXLj+fX46lrF7bmvrUrNbuVXAUxo8y5xMOi7Z37MDmHGJllOAIDnHmCcfm/Gz5ltrxxfj36y9MMf5L87cQ7gOFewUAwNQgOEagq0N2YvcEgUz+1sHPCo5Nfmsz5M9sYYrtx8cxNYIjpMmserXnvhYbv/fFUYmy4BCHr4vX41rBIXmm/s1GTHAIEqu8sUOmx9YhyefPOzxybFdQY1LOiuQRJ6lUvzqHL5dfjvMr4Ln6C1M4XDlygkOoGT9zP+Gw2PaoGR9Kqh1z7es51KG/4vEXOc36L1Wl8dGmXZfP+HfjhFz/1OAtWsWEe0QhDQDAHCA4RhI/UVBB0NvOFDn3JYe/ten/y1SN/VKaGsER20iVUbITUxIcetzbynK2du4GCA497m2JmViAiFNgVzHjFc/t6qY4ZifP2h+ympt7Kn+Nw6VpbH51UEoOl3zP1a+q/ML5xecW0pk65eqv+XdxuEqUBEfN+JlLcLht4/R92iG+mT/YGNC+6kRv85ryldT4DfkL4yOXv2r8ZcZP6tzu0/gv3l8qt1sBAMwBgmMi1BGPt1pNxdz2a1lKPWBZtFty1k6YWf2OV4UB7ir7Hv81W2oBAPYJgmMi7ovgKG25gvtJWA2OHS7HCYM0Dz981Pz9zT/C5xKPIQ3jHwAgD4JjIu664BChEbZYsf8XEsRbSmIHDNJ8+NmT4Nwv9dOrM/Rh/AMApEFwAADsGXHqa5807ONYPgEAAMaC4AAAAAAAgNlAcAAAAAAAwGwgOAAAAAAAYDYQHAAb2n+Vaf/G0vvXmbmxPzod83/8+66/0r6ronufhX17fI6l1H8st91/8hK3IW/7BwAA2BcIDuhxn52YpTi8Y18AtoT6x2+MH8Kc9Xdfuha9lG4qbqP/at4qDQAAsBQQHCO5qy9aQnC0Tp797qWdk90c1v3Wf5c3cc9ZfxUc8Zutx7RzidvoP/mb6vg65U3SAACwVBAcI2hXF/uOefeeDBEirzbvrOgLEnUS9J0WgmdD4+w7L2rsCyIYtvkj5yNXvg2zvP3taYhvz7lfnheWYrU6bi6uZOW7XQGXrTbX11fNxXE/79lltxXn2jiuq7PL5vryItiQOLUT2+jlv7pojid0vsSRFIdVPnWVXB1Y5el5t4ruraD34n953jx16uc5rGHF+/zZdpX+9KR9i/GUTwGOL67C9if51DaMt0OV2ncXwZGjdP6eg95z4CPBIYR2jvroEPovt9gh1z/vygEAgKWB4BiIrCKKIx6/gM+KBY0TJ946/erUa5h12DW/FSA2f4394GxsBIJ3nCvf5rF1UJL1M/ZzqOCwAkEc2+vLs20aObZbccS5VadWBIc4uRIfwoMdFTFtnhBu7MXHu6JCQx3R2MkVZ9Q6mvGx5JftMr14x6lNOayto7pq84VyWyfa2twFFRrbNg/ioeuvXPtakWKxonEXSuefFBSbdrz5hKN1+G36Q+m/0nUX4isXAgAAAG4DBMcAcjfy7glEFxevRIb8RiDY+Nb5769M9uIL9r1Vz9hmrnzNkxIcgt2y4dUnR/eEw9RPnlpknkLYePtdhYralFX49rcD/acddrVdHUxx+iyew5giOJImfWuzFRz2u6ZvBYm/Ch7iZdV7gMOqaTXec7J3IbSr6Q/bpqX2zYWF8B3bv+b8bRqNU2dej23Z1tE/lP6rve5SCyMAAAD7AMFRid7odxYciZVJb/+1tVmy34qLbjuVYuubK1/JCo64vKi+OWoER+usdlt2wgp5reBw8ob8kZO8C54jqbTOqeNQGyfWS3PbDmsObVcvrrZ9U4JjV6oEhxWA0tambeO08dOLQ+m/2t9p6HxQkxYAAGBuEBwDKW2p6gkC7wlDSnBEaUOYdfAL9r2nFTG7Cg5B48VWLl1MSXBY8eDGj3jCYdl1hV3wHEnFWyHvx990LvexQp4jKzgK7avs8wmHIEJCjiVNr62jtCoeuicgh9F/petTCNd5YS4AAAC4TRAcI9DVQ3vj9wSBOAfWwc8Kjk1+azPkz2xhiu3HxzE1giOkyayKtue+Fhu/98VRibLgEIe2i9fjWsEheab+zUaM50ha4lVzizrcfQd32G8ApnBYc+QEh1DTvvt8whHSBSGxFhuv+1uhvLTxbzCW3n/eokRMmAMKaQAAAG4bBMdI4icKKgh625ki577k8Lc2/X+ZqrFfSlMjOGIbqTJKdmJKgkOPe1t1ztbO6wDBoce9LT8TChDPkbSoA9lbwTfpxenchstq+km37cfLG9Jt8i9BcNS0794FxyYs7ic37Y2nHDf7YEn9V5w/KrdbAQAA3DYIjolQRzzeajUVc9uvZSn1APBQJ97+IPwuULNlEgAAYKkgOCbivgiO0pYrgH0Snh5sniR48QAAAHD7IDgm4q4LDhEaYYsV+8NhgYjQaLc6pf/GFgAAAPYDggMAAAAAAGbj4AQHAAAAAAAcDggOAAAAAACYDQQHAAAAAADMxlZw/M/TZ80333zTfPzxx27CQ0RegqUv0uPvXAEAAAAAbp+jh//1uPnsb39rvvjii+aDDz4In8fHx80777zjZjgU4rfyIjgAAAAAAG6foy+//LInMORTjkV4PHz48EaGpVB6EZb8jas+3RCWIDh4EzAAAAAA3DeOPvroIzdCwkWMLPFJR/v0otsudSPeESOd4Gg/23dK3BQssg1L4kK8Iw568e4TFCk7b593WQAAAADAfeHIC1SW+CNyeUogznzuSUV4G/ZvT3thKghs3vDEwTj/QQyYfPGx2LXlhviNKFH7VmR49diGZ57OAAAAAADcFQ5KcNQ46qmtU164fRLiPhWJfgcSY7dIufYzW6hqhBMAAAAAwKETBIcIixgNt4n3ifcEwSPl5BcFx2ablogAiy3PSzNacGxspeIBAAAAAO4Cd25LlWxz8n7bMeYJh0Xz936IbgTFEMHBlioAAAAAuC8cnOAQUj8az22B8gRB6TcbllaQdPn1eKjgCGXwo3EAAAAAuCccpOAQvCcSqR9pCyoIetuhorSlNPp0JYRL2S/XAmeA4Eg98QAAAAAAuKsciajI4WVaIqUtUQAAAAAAcPscvfvovUY5JIEBAAAAAADLB8EBAAAAAACzgeAAAAAAAIDZOHr48GGjiOCwxwAAAAAAALuA4AAAAAAAgNlAcAAAAAAAwGwgOAAAAAAAYDYQHAAAAAAAMBsIDgAAAAAAmA0EBwAAAAAAzAaCAwAAAAAAZgPBAQAAAAAAs4HgAAAAAACA2UBwAAAAAADAbCA4AAAAAABgNhAcAAAAAAAwGwgOAAAAAACYDQQHAAAAAADMBoIDAAAAAABmA8EBAAAAAACzcfTuo/caRQTHgwcPAAAAAAAAJgHBsQB+/vnnarz8S8er967nMjR/Lr3ExeTCl8QS6wTLJx7XNeTseHE54nw1NjTNmPKEsfnuA3O2jdiuJZW/JmwIQ/Pn0ktcTC58Tm6jDICxIDgmIp5YLF58nNf7Xoo7FLx6586l5jzH2LTEcd5xTbpabL5d8g49BqhhyDiSOMWLT5HLp2E5mzYuly7FmDz3hVLbSHwKLz7O630vxeXCU2mFXJwyxqYljvOOS+nGIDZSaHycB2Ap3EnB8fkPb5o3P3zuxs1NzYUfx9njIXE5Vqsnzbc/vml++Hzlxiua7s2P3zZPVvm0Y0jVWcJj4nh7HJOKHxoupMpOheeoSSPUpvOweVPfp2LI+Fh9/sNs4yhmKeO7RK78J9/+GPpMeLOn+UqJx07NWPLyeNg0mi4XVptHw2NS4SVi22OoHZdzUhpX9pxLeHntp0ccZ4+HxKXCNDwmjrfHMan4oeFCquxU+FR49qYuA2BKEBwTk5psLHGcHNdi8+VYgkOWqr9+xmm9Y82Xw+Ybktem9+JS4Tlq0gi16RRbh1o8O0MZMj7um+Cwjp3y47dPemlqyhc7bxLzlcxlsc2piOueI5U/d7xrmMXG59Km4kr2p6J2XN4GqXHltcXQsFx7xnFyXEspn4bbdF6YTVvC5huS16b34lLhU2Btxd9jNA5gvzxo/h9VaOiv1uoPrQAAAABJRU5ErkJggg==" alt="" />
MyRealm类:
@Service
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserSerivce userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String account= token.getUsername();
User user = userService.getUserByAccount(account);
return new SimpleAuthenticationInfo(user, user.getCpassword(), getName());
}
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();
shaCredentialsMatcher.setHashAlgorithmName("SHA-256");
shaCredentialsMatcher.setHashIterations("1");
super.setCredentialsMatcher(shaCredentialsMatcher);
}
}
上面securityManager是shiro的核心,其realm是我们自己定义的myRealm。doGetAuthorizationInfo跟授权相关我们先不看。大致上可以看出,doGetAuthenticationInfo方法返回的东西跟数据库中实际的用户名和密码有关,setCredentialsMatcher方法跟加密规则有关。
回到最开始的三行代码。subject.login(token),入参的token带有用户输入的账号密码,而我们的myRealm的方法返回值有数据库中该账号的密码。那么最后肯定是两者比较来进行认证。所以我们跟下subject.login方法。
DelegatingSubject:
public void login(AuthenticationToken token) throws AuthenticationException {
clearRunAsIdentitiesInternal();
Subject subject = securityManager.login(this, token);
......
}
因为我们的myRealm是在securityManager中,所以在跟下securityManager.login:
DefaultSecurityManager:
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
//看这里
info = authenticate(token);
} catch (AuthenticationException ae) {
try {
onFailedLogin(token, ae, subject);
} catch (Exception e) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an " +
"exception. Logging and propagating original AuthenticationException.", e);
}
}
throw ae; //propagate
}
Subject loggedIn = createSubject(token, info, subject);
onSuccessfulLogin(token, info, loggedIn);
return loggedIn;
}
前面MyRealm的doGetAuthenticationInfo方法返回的是SimpleAuthenticationInfo,其实是 AuthenticationInfo的子类,所以我再看authenticate()方法,因为使用了代理模式,实际调用在Authenticator类的子类
AbstractAuthenticator:
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
}
log.trace("Authentication attempt received for token [{}]", token);
AuthenticationInfo info;
try {
//看这里
info = doAuthenticate(token);
if (info == null) {
String msg = "No account information found for authentication token [" + token + "] by this " +
"Authenticator instance. Please check that it is configured correctly.";
throw new AuthenticationException(msg);
}
} catch (Throwable t) {
AuthenticationException ae = null;
if (t instanceof AuthenticationException) {
ae = (AuthenticationException) t;
}
if (ae == null) {
//Exception thrown was not an expected AuthenticationException. Therefore it is probably a little more
//severe or unexpected. So, wrap in an AuthenticationException, log to warn, and propagate:
String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " +
"error? (Typical or expected login exceptions should extend from AuthenticationException).";
ae = new AuthenticationException(msg, t);
if (log.isWarnEnabled())
log.warn(msg, t);
}
try {
notifyFailure(token, ae);
} catch (Throwable t2) {
if (log.isWarnEnabled()) {
String msg = "Unable to send notification for failed authentication attempt - listener error?. " +
"Please check your AuthenticationListener implementation(s). Logging sending exception " +
"and propagating original AuthenticationException instead...";
log.warn(msg, t2);
}
}
throw ae;
}
log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
notifySuccess(token, info);
return info;
}
又是一个方法返回AuthenticationInfo,再看下去。
ModularRealmAuthenticator:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();
if (realms.size() == 1) {
//看这里
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" +
token + "]. Please ensure that the appropriate Realm implementation is " +
"configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
}
//看这里
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
}
return info;
}
我们就一个realm所以看doSingleRealmAuthentication,这次终于看到我们熟悉的MyRealm里的方法了?不对,还少了个do。继续跟进。
AuthenticatingRealm:
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
//otherwise not cached, perform the lookup:
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
//not successful - throw an exception to indicate this:
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
这个方法才是重头。整体逻辑就是,先从缓存根据token取认证信息(AuthenticationInfo),若没有,则调用我们自己实现的MyRealm中的doGetAuthenticationInfo去获取,然后尝试缓存,最后再通过assertCredentialsMatch去验证token和info,assertCredentialsMatch则会根据MyReaml中setCredentialsMatcher我们设置的加密方式去进行相应的验证。
整个流程的牵涉到的uml图:
我们在xml配置文件中配置的MyRealm会被放到RealmSecurityManager的reals集合中,也就是说SercurityManager和下图中的AuthenticatingRealm是一对多的关系。
aaarticlea/png;base64,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" alt="" />
aaarticlea/png;base64,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" alt="" />
总结:一路跟踪下来其实学到不少,比如ModularRealmAuthenticator类中如果realm有多个那会走doMultiRealmAuthentication()方法,而ModularRealmAuthenticator类有个authenticationStrategy属性,在doMultiRealmAuthentication()方法中有用到,看名字就知道,使用了策略模式实现了各种realm的匹配规则。总而言之,流行起来的框架随便看一看都会有很多收获,希望有一天自己也能写出这样的代码。
- Shiro自定义realm实现密码验证及登录、密码加密注册、修改密码的验证
一:先从登录开始,直接看代码 @RequestMapping(value="dologin",method = {RequestMethod.GET, RequestMethod. ...
- Shiro固定身份验证
Shiro基础身份验证 如果要进行shiro的日志信息读取,那么需要使用一个org.apache.shiro.util.Factory接口,在这个接口里面定义有一 取得SecuruityManager ...
- 基于权限安全框架Shiro的登录验证功能实现
目前在企业级项目里做权限安全方面喜欢使用Apache开源的Shiro框架或者Spring框架的子框架Spring Security. Apache Shiro是一个强大且易用的Java安全框架,执行身 ...
- 第五章:shiro密码加密
在涉及到密码存储问题上,应该加密/生成密码摘要存储,而不是存储明文密码.比如之前的600w csdn账号泄露对用户可能造成很大损失,因此应加密/生成不可逆的摘要方式存储. 5.1 编码/解码 Shir ...
- shiro登陆权限验证
一>引入shirojar包 <!-- shiro登陆权限控制 --> <dependency> <groupId>org. ...
- Shiro笔记---身份验证
1.shiro有哪些主要功能 2.搭建shiro环境(*) idea2018.2.maven3.5.4.jdk1.8 项目结构: pom.xml: <dependencies> < ...
- Shiro -- (二) 身份验证基本流程
简介: 在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份: principals:身份,即主体的标识属性,可以是 ...
- SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)
导入依赖(pom.xml) <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...
- 基于Hadoop的密码安全级别验证
学习Hadoop有一段时间了,期间写过很多Demo,都是针对单个知识点做的验证,今天写个完整的应用程序——基于Hadoop的密码安全级别验证. 在很多网站上注册用户时输入密码都会在下方提示密码安全级别 ...
随机推荐
- (转)python中的参数:*args和**kwargs
def foo(*args, **kwargs):print 'args = ', argsprint 'kwargs = ', kwargsprint '---------------------- ...
- 五款超实用的开源 SVG 工具
英文链接:Idrsolutions SVG(Scalable Vector Graphics)是基于 XML 的矢量图像格式,用户可灵活运用图像进行搜索.索引.脚本以及压缩(来自:湖北教育考试网).S ...
- hdu4525
可以发现天的操作相当于*(k1+k2) 然后就很好判断了. 威威猫系列故事——吃鸡腿 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 6 ...
- will-change
目的: 让GPU分担CPU的工作,从而优化和分配内存,告知浏览器做好动画的准备. 背景: 注意事项: 1,will-change虽然可以加速,但是,一定一定要适度使用: 2,使用伪元素,独立渲染: 不 ...
- SG函数入门
sg[i]为0表示i节点先手必败. 首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数.例如mex{0,1,2,4}=3.mex{2 ...
- [黑金原创教程] FPGA那些事儿《设计篇 I》- 图像处理前夕
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- angular 2+ innerHTML属性中内联样式丢失
通过属性绑定的innerHTML,把字符串里面的html解析 解析是没问题的,但一些内联样式会丢失掉 为了不丢掉样式,需要自定义一个管道来解决这个问题 html.pipe.ts import {Pip ...
- ipad4没有声音提示消息
打开『设置』-『通用』-侧边开关用于: 1:锁定屏幕旋转 2:静音 √ 把对号去掉 选择1即可
- 160420、zTree获取所有选中节点数据
<!DOCTYPE html><HTML><HEAD> <TITLE> ZTREE DEMO - Standard Data </TITLE> ...
- Python--进阶处理9
# =========================第九章:元编程============================= # ----------------在函数上添加包装器--------- ...