-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
1062 lines (959 loc) · 343 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>z77z的小码窝</title>
<subtitle>年少无为,卖码为生。</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://z77z.oschina.io/"/>
<updated>2017-03-13T15:53:46.331Z</updated>
<id>http://z77z.oschina.io/</id>
<author>
<name>邹海清</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>SpringBoot+Shiro学习之密码加密和登录失败次数限制</title>
<link href="http://z77z.oschina.io/2017/03/13/SpringBoot+Shiro%E5%AD%A6%E4%B9%A0%E4%B9%8B%E5%AF%86%E7%A0%81%E5%8A%A0%E5%AF%86%E5%92%8C%E7%99%BB%E5%BD%95%E5%A4%B1%E8%B4%A5%E6%AC%A1%E6%95%B0%E9%99%90%E5%88%B6/"/>
<id>http://z77z.oschina.io/2017/03/13/SpringBoot+Shiro学习之密码加密和登录失败次数限制/</id>
<published>2017-03-13T15:53:22.000Z</published>
<updated>2017-03-13T15:53:46.331Z</updated>
<content type="html"><![CDATA[<p>这个项目写到现在,基本的雏形出来了,在此感谢一直关注的童鞋,送你们一句最近刚学习的一句鸡汤:<strong>念念不忘,必有回响。</strong>再贴一张ui图片:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170313201203428?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="z77z后台管理系统" title="">
</div>
<div class="image-caption">z77z后台管理系统</div>
</figure>
<p>·······················································································································································</p>
<p>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a></p>
<p>·······················································································································································</p>
<h2 id="前篇思考问题解决"><a href="#前篇思考问题解决" class="headerlink" title="前篇思考问题解决"></a>前篇思考问题解决</h2><hr>
<ol>
<li>前篇我们只是完成了同一账户的登录人数限制shiro拦截器的编写,对于手动踢出用户的功能只是说了采用在session域中添加一个key为kickout的布尔值,由之前编写的KickoutSessionControlFilter拦截器来判断是否将用户踢出,还没有说怎么获取当前在线用户的列表的核心代码,下面贴出来:</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 服务实现类</div><div class="line"> * </p></div><div class="line"> *</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> * <span class="doctag">@since</span> 2017-02-10</div><div class="line"> */</div><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SysUserService</span> <span class="keyword">extends</span> <span class="title">ServiceImpl</span><<span class="title">SysUserMapper</span>, <span class="title">SysUser</span>> </span>{</div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> RedisSessionDAO redisSessionDAO;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> Page<UserOnlineBo> <span class="title">getPagePlus</span><span class="params">(FrontPage<UserOnlineBo> frontPage)</span> </span>{</div><div class="line"> <span class="comment">// 因为我们是用redis实现了shiro的session的Dao,而且是采用了shiro+redis这个插件</span></div><div class="line"> <span class="comment">// 所以从spring容器中获取redisSessionDAO</span></div><div class="line"> <span class="comment">// 来获取session列表.</span></div><div class="line"> Collection<Session> sessions = redisSessionDAO.getActiveSessions();</div><div class="line"> Iterator<Session> it = sessions.iterator();</div><div class="line"> List<UserOnlineBo> onlineUserList = <span class="keyword">new</span> ArrayList<UserOnlineBo>();</div><div class="line"> Page<UserOnlineBo> pageList = frontPage.getPagePlus();</div><div class="line"> <span class="comment">// 遍历session</span></div><div class="line"> <span class="keyword">while</span> (it.hasNext()) {</div><div class="line"> <span class="comment">// 这是shiro已经存入session的</span></div><div class="line"> <span class="comment">// 现在直接取就是了</span></div><div class="line"> Session session = it.next();</div><div class="line"> <span class="comment">// 如果被标记为踢出就不显示</span></div><div class="line"> Object obj = session.getAttribute(<span class="string">"kickout"</span>);</div><div class="line"> <span class="keyword">if</span> (obj != <span class="keyword">null</span>)</div><div class="line"> <span class="keyword">continue</span>;</div><div class="line"> UserOnlineBo onlineUser = getSessionBo(session);</div><div class="line"> onlineUserList.add(onlineUser);</div><div class="line"> }</div><div class="line"> <span class="comment">// 再将List<UserOnlineBo>转换成mybatisPlus封装的page对象</span></div><div class="line"> <span class="keyword">int</span> page = frontPage.getPage() - <span class="number">1</span>;</div><div class="line"> <span class="keyword">int</span> rows = frontPage.getRows() - <span class="number">1</span>;</div><div class="line"> <span class="keyword">int</span> startIndex = page * rows;</div><div class="line"> <span class="keyword">int</span> endIndex = (page * rows) + rows;</div><div class="line"> <span class="keyword">int</span> size = onlineUserList.size();</div><div class="line"> <span class="keyword">if</span> (endIndex > size) {</div><div class="line"> endIndex = size;</div><div class="line"> }</div><div class="line"> pageList.setRecords(onlineUserList.subList(startIndex, endIndex));</div><div class="line"> pageList.setTotal(size);</div><div class="line"> <span class="keyword">return</span> pageList;</div><div class="line"> }</div><div class="line"> <span class="comment">//从session中获取UserOnline对象</span></div><div class="line"> <span class="function"><span class="keyword">private</span> UserOnlineBo <span class="title">getSessionBo</span><span class="params">(Session session)</span></span>{</div><div class="line"> <span class="comment">//获取session登录信息。</span></div><div class="line"> Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);</div><div class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == obj){</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">//确保是 SimplePrincipalCollection对象。</span></div><div class="line"> <span class="keyword">if</span>(obj <span class="keyword">instanceof</span> SimplePrincipalCollection){</div><div class="line"> SimplePrincipalCollection spc = (SimplePrincipalCollection)obj;</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 获取用户登录的,<span class="doctag">@link</span> SampleRealm.doGetAuthenticationInfo(...)方法中</div><div class="line"> * return new SimpleAuthenticationInfo(user,user.getPswd(), getName());的user 对象。</div><div class="line"> */</div><div class="line"> obj = spc.getPrimaryPrincipal();</div><div class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> != obj && obj <span class="keyword">instanceof</span> SysUser){</div><div class="line"> <span class="comment">//存储session + user 综合信息</span></div><div class="line"> UserOnlineBo userBo = <span class="keyword">new</span> UserOnlineBo((SysUser)obj);</div><div class="line"> <span class="comment">//最后一次和系统交互的时间</span></div><div class="line"> userBo.setLastAccess(session.getLastAccessTime());</div><div class="line"> <span class="comment">//主机的ip地址</span></div><div class="line"> userBo.setHost(session.getHost());</div><div class="line"> <span class="comment">//session ID</span></div><div class="line"> userBo.setSessionId(session.getId().toString());</div><div class="line"> <span class="comment">//session最后一次与系统交互的时间</span></div><div class="line"> userBo.setLastLoginTime(session.getLastAccessTime());</div><div class="line"> <span class="comment">//回话到期 ttl(ms)</span></div><div class="line"> userBo.setTimeout(session.getTimeout());</div><div class="line"> <span class="comment">//session创建时间</span></div><div class="line"> userBo.setStartTime(session.getStartTimestamp());</div><div class="line"> <span class="comment">//是否踢出</span></div><div class="line"> userBo.setSessionStatus(<span class="keyword">false</span>);</div><div class="line"> <span class="keyword">return</span> userBo;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>代码中注释比较完善,也可以去下载源码查看,这样结合看,跟容易理解,不懂的在评论区留言,看见必回!</p>
<ol>
<li>对Ajax请求的优化:这里有一个前提,我们知道Ajax不能做页面redirect和forward跳转,所以Ajax请求假如没登录,那么这个请求给用户的感觉就是没有任何反应,而用户又不知道用户已经退出了。也就是说在KickoutSessionControlFilter拦截器拦截后,正常如果被踢出,就会跳转到被踢出的提示页面,如果是Ajax请求,给用户的感觉就是没有感觉,核心解决代码如下:</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">Map<String, String> resultMap = <span class="keyword">new</span> HashMap<String, String>();</div><div class="line"><span class="comment">//判断是不是Ajax请求</span></div><div class="line"><span class="keyword">if</span> (<span class="string">"XMLHttpRequest"</span>.equalsIgnoreCase(((HttpServletRequest) request).getHeader(<span class="string">"X-Requested-With"</span>))) {</div><div class="line"> resultMap.put(<span class="string">"user_status"</span>, <span class="string">"300"</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"您已经在其他地方登录,请重新登录!"</span>);</div><div class="line"> <span class="comment">//输出json串</span></div><div class="line"> out(response, resultMap);</div><div class="line">}<span class="keyword">else</span>{</div><div class="line"> <span class="comment">//重定向</span></div><div class="line"> WebUtils.issueRedirect(request, response, kickoutUrl);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">out</span><span class="params">(ServletResponse hresponse, Map<String, String> resultMap)</span></span></div><div class="line"> <span class="keyword">throws</span> IOException {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> hresponse.setCharacterEncoding(<span class="string">"UTF-8"</span>);</div><div class="line"> PrintWriter out = hresponse.getWriter();</div><div class="line"> out.println(JSON.toJSONString(resultMap));</div><div class="line"> out.flush();</div><div class="line"> out.close();</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> System.err.println(<span class="string">"KickoutSessionFilter.class 输出JSON异常,可以忽略。"</span>);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这是在KickoutSessionControlFilter这个拦截器里面做的修改。</p>
<h2 id="目标:"><a href="#目标:" class="headerlink" title="目标:"></a>目标:</h2><hr>
<ol>
<li><p>现在项目里面的密码整个流程都是以明文的方式传递的。这样在实际应用中是很不安全的,京东,开源中国等这些大公司都有泄库事件,这样对用户的隐私造成巨大的影响,所以将密码加密存储传输就非常必要了。</p>
</li>
<li><p>密码重试次数限制,也是出于安全性的考虑。</p>
</li>
</ol>
<h2 id="实现目标一:"><a href="#实现目标一:" class="headerlink" title="实现目标一:"></a>实现目标一:</h2><hr>
<p>shiro本身是有对密码加密进行实现的,提供了PasswordService及CredentialsMatcher用于提供加密密码及验证密码服务。这里我觉得这种过于麻烦,大家有需要可以去看这篇博客:<a href="http://jinnianshilongnian.iteye.com/blog/2021439" target="_blank" rel="external">shiro加密解密</a>。</p>
<p>我就是自己实现的EDS加密,并且保存的加密明文是采用password+username的方式,减小了密码相同,密文也相同的问题,这里我只是贴一下,EDS的加密解密代码,另外我还改了MyShiroRealm文件,再查数据库的时候加密后再查,而且在创建用户的时候不要忘记的加密存到数据库。这里就补贴代码了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * DES加密解密</div><div class="line"> * </div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> * <span class="doctag">@datetime</span> 2017-3-13</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyDES</span> </span>{</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * DES算法密钥</div><div class="line"> */</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">byte</span>[] DES_KEY = { <span class="number">21</span>, <span class="number">1</span>, -<span class="number">110</span>, <span class="number">82</span>, -<span class="number">32</span>, -<span class="number">85</span>, -<span class="number">128</span>, -<span class="number">65</span> };</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 数据加密,算法(DES)</div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> data</div><div class="line"> * 要进行加密的数据</div><div class="line"> * <span class="doctag">@return</span> 加密后的数据</div><div class="line"> */</div><div class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"restriction"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">encryptBasedDes</span><span class="params">(String data)</span> </span>{</div><div class="line"> String encryptedData = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">// DES算法要求有一个可信任的随机数源</span></div><div class="line"> SecureRandom sr = <span class="keyword">new</span> SecureRandom();</div><div class="line"> DESKeySpec deskey = <span class="keyword">new</span> DESKeySpec(DES_KEY);</div><div class="line"> <span class="comment">// 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象</span></div><div class="line"> SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(<span class="string">"DES"</span>);</div><div class="line"> SecretKey key = keyFactory.generateSecret(deskey);</div><div class="line"> <span class="comment">// 加密对象</span></div><div class="line"> Cipher cipher = Cipher.getInstance(<span class="string">"DES"</span>);</div><div class="line"> cipher.init(Cipher.ENCRYPT_MODE, key, sr);</div><div class="line"> <span class="comment">// 加密,并把字节数组编码成字符串</span></div><div class="line"> encryptedData = <span class="keyword">new</span> sun.misc.BASE64Encoder().encode(cipher.doFinal(data.getBytes()));</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="comment">// log.error("加密错误,错误信息:", e);</span></div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"加密错误,错误信息:"</span>, e);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> encryptedData;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 数据解密,算法(DES)</div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> cryptData</div><div class="line"> * 加密数据</div><div class="line"> * <span class="doctag">@return</span> 解密后的数据</div><div class="line"> */</div><div class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"restriction"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">decryptBasedDes</span><span class="params">(String cryptData)</span> </span>{</div><div class="line"> String decryptedData = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">// DES算法要求有一个可信任的随机数源</span></div><div class="line"> SecureRandom sr = <span class="keyword">new</span> SecureRandom();</div><div class="line"> DESKeySpec deskey = <span class="keyword">new</span> DESKeySpec(DES_KEY);</div><div class="line"> <span class="comment">// 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象</span></div><div class="line"> SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(<span class="string">"DES"</span>);</div><div class="line"> SecretKey key = keyFactory.generateSecret(deskey);</div><div class="line"> <span class="comment">// 解密对象</span></div><div class="line"> Cipher cipher = Cipher.getInstance(<span class="string">"DES"</span>);</div><div class="line"> cipher.init(Cipher.DECRYPT_MODE, key, sr);</div><div class="line"> <span class="comment">// 把字符串解码为字节数组,并解密</span></div><div class="line"> decryptedData = <span class="keyword">new</span> String(cipher.doFinal(<span class="keyword">new</span> sun.misc.BASE64Decoder().decodeBuffer(cryptData)));</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="comment">// log.error("解密错误,错误信息:", e);</span></div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"解密错误,错误信息:"</span>, e);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> decryptedData;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> String str = <span class="string">"123456"</span>;</div><div class="line"> <span class="comment">// DES数据加密</span></div><div class="line"> String s1 = encryptBasedDes(str);</div><div class="line"> System.out.println(s1);</div><div class="line"> <span class="comment">// DES数据解密</span></div><div class="line"> String s2 = decryptBasedDes(s1);</div><div class="line"> System.err.println(s2);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="实现目标二"><a href="#实现目标二" class="headerlink" title="实现目标二"></a>实现目标二</h2><hr>
<p>如在1个小时内密码最多重试5次,如果尝试次数超过5次就锁定1小时,1小时后可再次重试,如果还是重试失败,可以锁定如1天,以此类推,防止密码被暴力破解。我们使用redis数据库来保存当前用户登录次数,也就是执行身份认证方法:MyShiroRealm.doGetAuthenticationInfo()的次数,如果登录成功就清空计数。超过就返回相应错误信息。根据这个逻辑,修改MyShiroRealm.java如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 认证信息.(身份验证) : Authentication 是用来验证用户身份</div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> token</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> * <span class="doctag">@throws</span> AuthenticationException</div><div class="line"> */</div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">protected</span> AuthenticationInfo <span class="title">doGetAuthenticationInfo</span><span class="params">(</span></span></div><div class="line"> AuthenticationToken authcToken) <span class="keyword">throws</span> AuthenticationException {</div><div class="line"> </div><div class="line"> </div><div class="line"> System.out.println(<span class="string">"身份认证方法:MyShiroRealm.doGetAuthenticationInfo()"</span>);</div><div class="line"> </div><div class="line"> UsernamePasswordToken token = (UsernamePasswordToken) authcToken;</div><div class="line"> String name = token.getUsername();</div><div class="line"> String password = String.valueOf(token.getPassword());</div><div class="line"> <span class="comment">//访问一次,计数一次</span></div><div class="line"> ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();</div><div class="line"> opsForValue.increment(SHIRO_LOGIN_COUNT+name, <span class="number">1</span>);</div><div class="line"> <span class="comment">//计数大于5时,设置用户被锁定一小时</span></div><div class="line"> <span class="keyword">if</span>(Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT+name))>=<span class="number">5</span>){</div><div class="line"> opsForValue.set(SHIRO_IS_LOCK+name, <span class="string">"LOCK"</span>);</div><div class="line"> stringRedisTemplate.expire(SHIRO_IS_LOCK+name, <span class="number">1</span>, TimeUnit.HOURS);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (<span class="string">"LOCK"</span>.equals(opsForValue.get(SHIRO_IS_LOCK+name))){</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> DisabledAccountException(<span class="string">"由于密码输入错误次数大于5次,帐号已经禁止登录!"</span>);</div><div class="line"> }</div><div class="line"> Map<String, Object> map = <span class="keyword">new</span> HashMap<String, Object>();</div><div class="line"> map.put(<span class="string">"nickname"</span>, name);</div><div class="line"> <span class="comment">//密码进行加密处理 明文为 password+name</span></div><div class="line"> String paw = password+name;</div><div class="line"> String pawDES = MyDES.encryptBasedDes(paw);</div><div class="line"> map.put(<span class="string">"pswd"</span>, pawDES);</div><div class="line"> SysUser user = <span class="keyword">null</span>;</div><div class="line"> <span class="comment">// 从数据库获取对应用户名密码的用户</span></div><div class="line"> List<SysUser> userList = sysUserService.selectByMap(map);</div><div class="line"> <span class="keyword">if</span>(userList.size()!=<span class="number">0</span>){</div><div class="line"> user = userList.get(<span class="number">0</span>);</div><div class="line"> } </div><div class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> == user) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> AccountException(<span class="string">"帐号或密码不正确!"</span>);</div><div class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(user.getStatus()==<span class="number">0</span>){</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 如果用户的status为禁用。那么就抛出<code>DisabledAccountException</code></div><div class="line"> */</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> DisabledAccountException(<span class="string">"此帐号已经设置为禁止登录!"</span>);</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> <span class="comment">//登录成功</span></div><div class="line"> <span class="comment">//更新登录时间 last login time</span></div><div class="line"> user.setLastLoginTime(<span class="keyword">new</span> Date());</div><div class="line"> sysUserService.updateById(user);</div><div class="line"> <span class="comment">//清空登录计数</span></div><div class="line"> opsForValue.set(SHIRO_LOGIN_COUNT+name, <span class="string">"0"</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> SimpleAuthenticationInfo(user, password, getName());</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>这个项目写到现在,基本的雏形出来了,在此感谢一直关注的童鞋,送你们一句最近刚学习的一句鸡汤:<strong>念念不忘,必有回响。</strong>再贴一张ui图片:</p>
<figure class="image-bubble">
<div
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="redis" scheme="http://z77z.oschina.io/tags/redis/"/>
<category term="shiro" scheme="http://z77z.oschina.io/tags/shiro/"/>
</entry>
<entry>
<title>SpringBoot+Shiro学习之自定义拦截器管理在线用户(踢出用户)</title>
<link href="http://z77z.oschina.io/2017/03/05/SpringBoot+Shiro%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8B%A6%E6%88%AA%E5%99%A8%E7%AE%A1%E7%90%86%E5%9C%A8%E7%BA%BF%E7%94%A8%E6%88%B7%EF%BC%88%E8%B8%A2%E5%87%BA%E7%94%A8%E6%88%B7%EF%BC%89/"/>
<id>http://z77z.oschina.io/2017/03/05/SpringBoot+Shiro学习之自定义拦截器管理在线用户(踢出用户)/</id>
<published>2017-03-05T09:27:14.000Z</published>
<updated>2017-03-05T09:27:36.077Z</updated>
<content type="html"><![CDATA[<h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ol>
<li><p>我们经常会有用到,当A 用户在北京登录 ,然后A用户在天津再登录 ,要踢出北京登录的状态。如果用户在北京重新登录,那么又要踢出天津的用户,这样反复。又或是需要限制同一用户的同时在线数量,超出限制后,踢出最先登录的或是踢出最后登录的。</p>
</li>
<li><p>第一个场景踢出用户是由用户触发的,有时候需要手动将某个在线用户踢出,也就是对当前在线用户的列表进行管理。</p>
</li>
</ol>
<p>·························································································································································<br>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a><br>························································································································································</p>
<h2 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h2><p>spring security就直接提供了相应的功能;Shiro的话没有提供默认实现,不过可以很容易的在Shiro中加入这个功能。那就是使用shiro强大的自定义访问控制拦截器:AccessControlFilter,集成这个接口后要实现下面这三个方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">abstract</span> <span class="keyword">boolean</span> <span class="title">isAccessAllowed</span><span class="params">(ServletRequest request, ServletResponse response, Object mappedValue)</span> <span class="keyword">throws</span> Exception</span>; </div><div class="line"></div><div class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">onAccessDenied</span><span class="params">(ServletRequest request, ServletResponse response, Object mappedValue)</span> <span class="keyword">throws</span> Exception</span>; </div><div class="line"> </div><div class="line"><span class="function"><span class="keyword">abstract</span> <span class="keyword">boolean</span> <span class="title">onAccessDenied</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="keyword">throws</span> Exception</span>;</div></pre></td></tr></table></figure>
<p>isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;</p>
<p>onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。</p>
<p>onPreHandle:会自动调用这两个方法决定是否继续处理;</p>
<p>另外AccessControlFilter还提供了如下方法用于处理如登录成功后/重定向到上一个请求: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">setLoginUrl</span><span class="params">(String loginUrl)</span> <span class="comment">//身份验证时使用,默认/login.jsp </span></span></div><div class="line">String <span class="title">getLoginUrl</span><span class="params">()</span> </div><div class="line">Subject <span class="title">getSubject</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="comment">//获取Subject实例 </span></div><div class="line"><span class="keyword">boolean</span> <span class="title">isLoginRequest</span><span class="params">(ServletRequest request, ServletResponse response)</span><span class="comment">//当前请求是否是登录请求 </span></div><div class="line"><span class="keyword">void</span> <span class="title">saveRequestAndRedirectToLogin</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="keyword">throws</span> IOException <span class="comment">//将当前请求保存起来并重定向到登录页面 </span></div><div class="line"><span class="keyword">void</span> <span class="title">saveRequest</span><span class="params">(ServletRequest request)</span> <span class="comment">//将请求保存起来,如登录成功后再重定向回该请求 </span></div><div class="line"><span class="keyword">void</span> <span class="title">redirectToLogin</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="comment">//重定向到登录页面</span></div></pre></td></tr></table></figure>
<p>比如基于表单的身份验证就需要使用这些功能。</p>
<p>到此基本的拦截器就完事了,如果我们想进行访问的控制就可以继承AccessControlFilter;如果我们要添加一些通用数据我们可以直接继承PathMatchingFilter。</p>
<p>下面就是我实现的访问控制拦截器:KickoutSessionControlFilter:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <span class="doctag">@author</span> 作者 z77z</div><div class="line"> * <span class="doctag">@date</span> 创建时间:2017年3月5日 下午1:16:38</div><div class="line"> * 思路:</div><div class="line"> * 1.读取当前登录用户名,获取在缓存中的sessionId队列</div><div class="line"> * 2.判断队列的长度,大于最大登录限制的时候,按踢出规则</div><div class="line"> * 将之前的sessionId中的session域中存入kickout:true,并更新队列缓存</div><div class="line"> * 3.判断当前登录的session域中的kickout如果为true,</div><div class="line"> * 想将其做退出登录处理,然后再重定向到踢出登录提示页面</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">KickoutSessionControlFilter</span> <span class="keyword">extends</span> <span class="title">AccessControlFilter</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String kickoutUrl; <span class="comment">//踢出后到的地址</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">boolean</span> kickoutAfter = <span class="keyword">false</span>; <span class="comment">//踢出之前登录的/之后登录的用户 默认踢出之前登录的用户</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> maxSession = <span class="number">1</span>; <span class="comment">//同一个帐号最大会话数 默认1</span></div><div class="line"></div><div class="line"> <span class="keyword">private</span> SessionManager sessionManager;</div><div class="line"> <span class="keyword">private</span> Cache<String, Deque<Serializable>> cache;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setKickoutUrl</span><span class="params">(String kickoutUrl)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.kickoutUrl = kickoutUrl;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setKickoutAfter</span><span class="params">(<span class="keyword">boolean</span> kickoutAfter)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.kickoutAfter = kickoutAfter;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMaxSession</span><span class="params">(<span class="keyword">int</span> maxSession)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.maxSession = maxSession;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSessionManager</span><span class="params">(SessionManager sessionManager)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.sessionManager = sessionManager;</div><div class="line"> }</div><div class="line"> <span class="comment">//设置Cache的key的前缀</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCacheManager</span><span class="params">(CacheManager cacheManager)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.cache = cacheManager.getCache(<span class="string">"shiro_redis_cache"</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isAccessAllowed</span><span class="params">(ServletRequest request, ServletResponse response, Object mappedValue)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">onAccessDenied</span><span class="params">(ServletRequest request, ServletResponse response)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> Subject subject = getSubject(request, response);</div><div class="line"> <span class="keyword">if</span>(!subject.isAuthenticated() && !subject.isRemembered()) {</div><div class="line"> <span class="comment">//如果没有登录,直接进行之后的流程</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> Session session = subject.getSession();</div><div class="line"> SysUser user = (SysUser) subject.getPrincipal();</div><div class="line"> String username = user.getNickname();</div><div class="line"> Serializable sessionId = session.getId();</div><div class="line"></div><div class="line"> <span class="comment">//读取缓存 没有就存入</span></div><div class="line"> Deque<Serializable> deque = cache.get(username);</div><div class="line"> </div><div class="line"> <span class="comment">//如果队列里没有此sessionId,且用户没有被踢出;放入队列</span></div><div class="line"> <span class="keyword">if</span>(!deque.contains(sessionId) && session.getAttribute(<span class="string">"kickout"</span>) == <span class="keyword">null</span>) {</div><div class="line"> <span class="comment">//将sessionId存入队列</span></div><div class="line"> deque.push(sessionId);</div><div class="line"> <span class="comment">//将用户的sessionId队列缓存</span></div><div class="line"> cache.put(username, deque);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">//如果队列里的sessionId数超出最大会话数,开始踢人</span></div><div class="line"> <span class="keyword">while</span>(deque.size() > maxSession) {</div><div class="line"> Serializable kickoutSessionId = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">if</span>(kickoutAfter) { <span class="comment">//如果踢出后者</span></div><div class="line"> kickoutSessionId = deque.removeFirst();</div><div class="line"> } <span class="keyword">else</span> { <span class="comment">//否则踢出前者</span></div><div class="line"> kickoutSessionId = deque.removeLast();</div><div class="line"> }</div><div class="line"> <span class="comment">//踢出后再更新下缓存队列</span></div><div class="line"> cache.put(username, deque);</div><div class="line"> </div><div class="line"> </div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">//获取被踢出的sessionId的session对象</span></div><div class="line"> Session kickoutSession = sessionManager.getSession(<span class="keyword">new</span> DefaultSessionKey(kickoutSessionId));</div><div class="line"> <span class="keyword">if</span>(kickoutSession != <span class="keyword">null</span>) {</div><div class="line"> <span class="comment">//设置会话的kickout属性表示踢出了</span></div><div class="line"> kickoutSession.setAttribute(<span class="string">"kickout"</span>, <span class="keyword">true</span>);</div><div class="line"> }</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {<span class="comment">//ignore exception</span></div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">//如果被踢出了,直接退出,重定向到踢出后的地址</span></div><div class="line"> <span class="keyword">if</span> ((Boolean)session.getAttribute(<span class="string">"kickout"</span>)!=<span class="keyword">null</span>&&(Boolean)session.getAttribute(<span class="string">"kickout"</span>) == <span class="keyword">true</span>) {</div><div class="line"> <span class="comment">//会话被踢出了</span></div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">//退出登录</span></div><div class="line"> subject.logout();</div><div class="line"> } <span class="keyword">catch</span> (Exception e) { <span class="comment">//ignore</span></div><div class="line"> }</div><div class="line"> saveRequest(request);</div><div class="line"> <span class="comment">//重定向</span></div><div class="line"> WebUtils.issueRedirect(request, response, kickoutUrl);</div><div class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>将这个自定义的拦截器配置在ShiroConfig.java文件中:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 限制同一账号登录同时登录人数控制</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> KickoutSessionControlFilter <span class="title">kickoutSessionControlFilter</span><span class="params">()</span></span>{</div><div class="line"> KickoutSessionControlFilter kickoutSessionControlFilter = <span class="keyword">new</span> KickoutSessionControlFilter();</div><div class="line"> <span class="comment">//使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;</span></div><div class="line"> <span class="comment">//这里我们还是用之前shiro使用的redisManager()实现的cacheManager()缓存管理</span></div><div class="line"> <span class="comment">//也可以重新另写一个,重新配置缓存时间之类的自定义缓存属性</span></div><div class="line"> kickoutSessionControlFilter.setCacheManager(cacheManager());</div><div class="line"> <span class="comment">//用于根据会话ID,获取会话进行踢出操作的;</span></div><div class="line"> kickoutSessionControlFilter.setSessionManager(sessionManager());</div><div class="line"> <span class="comment">//是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;踢出顺序。</span></div><div class="line"> kickoutSessionControlFilter.setKickoutAfter(<span class="keyword">false</span>);</div><div class="line"> <span class="comment">//同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;</span></div><div class="line"> kickoutSessionControlFilter.setMaxSession(<span class="number">1</span>);</div><div class="line"> <span class="comment">//被踢出后重定向到的地址;</span></div><div class="line"> kickoutSessionControlFilter.setKickoutUrl(<span class="string">"/kickout"</span>);</div><div class="line"> <span class="keyword">return</span> kickoutSessionControlFilter;</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>将这个kickoutSessionControlFilter()注入到shiroFilterFactoryBean中:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//自定义拦截器</span></div><div class="line">Map<String, Filter> filtersMap = <span class="keyword">new</span> LinkedHashMap<String, Filter>();</div><div class="line"><span class="comment">//限制同一帐号同时在线的个数。</span></div><div class="line">filtersMap.put(<span class="string">"kickout"</span>, kickoutSessionControlFilter());</div><div class="line">shiroFilterFactoryBean.setFilters(filtersMap);</div></pre></td></tr></table></figure>
<p>由于我们链接权限的控制是动态存在数据库中的,这个可以去看我之前动态权限控制的博文,所以我们还要在数据库中修改链接的权限,将kickout这个自定义的权限配置在对应的链接上。如下图:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170305165123133?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="权限表" title="">
</div>
<div class="image-caption">权限表</div>
</figure>
<p>还要编写对应的被踢出的跳转页面:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=UTF-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"UTF-8"</span>%></div><div class="line"><span class="tag"><<span class="name">%</span></span></div><div class="line"> <span class="attr">String</span> <span class="attr">path</span> = <span class="string">request.getContextPath();</span></div><div class="line"> <span class="attr">String</span> <span class="attr">basePath</span> = <span class="string">request.getScheme()</span> + "<span class="attr">:</span>//"</div><div class="line"> + <span class="attr">request.getServerName</span>() + "<span class="attr">:</span>" + <span class="attr">request.getServerPort</span>()</div><div class="line"> + <span class="attr">path</span>;</div><div class="line">%></div><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=UTF-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span></span></div><div class="line"> <span class="attr">src</span>=<span class="string">"<%=basePath%>/static/js/jquery-1.11.3.js"</span>><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>被踢出<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line">被踢出 或则在另一地方登录,或已经达到此账号登录上限被挤掉。</div><div class="line"><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">id</span>=<span class="string">"login"</span> <span class="attr">value</span>=<span class="string">"重新登录"</span> /></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="xml"></span></div><div class="line">$("#login").click(function(){</div><div class="line"> window.open("<span class="tag"><<span class="name">%=basePath%</span>></span>/login"); </div><div class="line">});</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<p>到此,第一个场景就实现了,写到这里实际第二个场景的实现思路已经就很明显了,可以通过sessionDAO获取到全部的shiro会话List,然后显示在前端页面,踢出对应用户就可以使用在对应sessionId的session域中设置key为kickout的值为true,上面的KickoutSessionControlFilter就会判断session域中的kickout值,做响应的处理。这里我就先不上代码了,大家可以自己试一试。之后再把代码同步到我的码云上,供大家学习交流。</p>
<p>处理了这个需求后,我发现一个问题,这里有一个前提,我们知道Ajax不能做页面redirect和forward跳转,所以Ajax请求假如没登录,那么这个请求给用户的感觉就是没有任何反应,而用户又不知道用户已经退出了。这个就要对ajax请求做相应的优化,我已经有解决思路了,大家也可以思考下,我也会在下一博提供代码。</p>
<p>还有我接下来会对之前的前端页面进行完善,比如下面是我更新的登录页面:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170305171317582?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="登录页面" title="">
</div>
<div class="image-caption">登录页面</div>
</figure>
<p>已经更新到我的码云上面。</p>
]]></content>
<summary type="html">
<h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ol>
<li><p>我们经常会有用到,当A 用户在北京登录 ,然后A用户在天津再登录 ,要踢出北京登录的状态。如果用户在北京重
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="redis" scheme="http://z77z.oschina.io/tags/redis/"/>
<category term="shiro" scheme="http://z77z.oschina.io/tags/shiro/"/>
</entry>
<entry>
<title>SpringBoot+Shiro学习之“记住我”和“GIF验证码”功能的实现</title>
<link href="http://z77z.oschina.io/2017/02/26/SpringBoot+Shiro%E5%AD%A6%E4%B9%A0%E4%B9%8B%E2%80%9C%E8%AE%B0%E4%BD%8F%E6%88%91%E2%80%9D%E5%92%8C%E2%80%9CGIF%E9%AA%8C%E8%AF%81%E7%A0%81%E2%80%9D%E5%8A%9F%E8%83%BD%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
<id>http://z77z.oschina.io/2017/02/26/SpringBoot+Shiro学习之“记住我”和“GIF验证码”功能的实现/</id>
<published>2017-02-26T10:43:44.000Z</published>
<updated>2017-02-26T11:21:07.645Z</updated>
<content type="html"><![CDATA[<h2 id="学习目标"><a href="#学习目标" class="headerlink" title="学习目标"></a>学习目标</h2><p>如标题有如下两个功能实现:</p>
<ul>
<li><p>记住我的功能:通过设置key为“rememberMe”的cookie保存在客户端来完成记住我的功能,下次用户访问指定页面时就不会重新登录,一直到cookie过期后才会重新登录。</p>
</li>
<li><p>GIF格式验证码: <figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170226175201770?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>,这个要感谢sojson的博主对这个GIF验证码插件的实现。</p>
</li>
</ul>
<p>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a></p>
<h2 id="记住我"><a href="#记住我" class="headerlink" title="记住我"></a>记住我</h2><ul>
<li>ShiroConfig的配置:</li>
</ul>
<p>在ShiroConfig.java中添加如下方法:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * cookie对象;</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> SimpleCookie <span class="title">rememberMeCookie</span><span class="params">()</span></span>{</div><div class="line"> <span class="comment">//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe</span></div><div class="line"> SimpleCookie simpleCookie = <span class="keyword">new</span> SimpleCookie(<span class="string">"rememberMe"</span>);</div><div class="line"> <span class="comment">//<!-- 记住我cookie生效时间30天 ,单位秒;--></span></div><div class="line"> simpleCookie.setMaxAge(<span class="number">2592000</span>);</div><div class="line"> <span class="keyword">return</span> simpleCookie;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * cookie管理对象;记住我功能</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">public</span> CookieRememberMeManager <span class="title">rememberMeManager</span><span class="params">()</span></span>{</div><div class="line"> CookieRememberMeManager cookieRememberMeManager = <span class="keyword">new</span> CookieRememberMeManager();</div><div class="line"> cookieRememberMeManager.setCookie(rememberMeCookie());</div><div class="line"> <span class="comment">//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)</span></div><div class="line"> cookieRememberMeManager.setCipherKey(Base64.decode(<span class="string">"3AvVhmFLUs0KTA3Kprsdag=="</span>));</div><div class="line"> <span class="keyword">return</span> cookieRememberMeManager;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>rememberMeCookie()方法是设置Cookie的生成模版,比如cookie的name,cookie的有效时间等等。<br>rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中,如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span></div><div class="line"><span class="function"><span class="keyword">public</span> SecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>{</div><div class="line"> DefaultWebSecurityManager securityManager = <span class="keyword">new</span> DefaultWebSecurityManager();</div><div class="line"> <span class="comment">// 设置realm.</span></div><div class="line"> securityManager.setRealm(myShiroRealm());</div><div class="line"> <span class="comment">// 自定义缓存实现 使用redis</span></div><div class="line"> securityManager.setCacheManager(cacheManager());</div><div class="line"> <span class="comment">// 自定义session管理 使用redis</span></div><div class="line"> securityManager.setSessionManager(SessionManager());</div><div class="line"> <span class="comment">//注入记住我管理器;</span></div><div class="line"> securityManager.setRememberMeManager(rememberMeManager());</div><div class="line"> <span class="keyword">return</span> securityManager;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>其实上面的步骤,也就是rememberMe管理器可以不用配置,shiro会使用默认的配置,之所以要配置的目的是为了能够在实际业务环境中自定义其中的参数。</p>
<ul>
<li>登录controller的改造</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"ajaxLogin"</span>,method=RequestMethod.POST)</div><div class="line"><span class="meta">@ResponseBody</span></div><div class="line"><span class="function"><span class="keyword">public</span> Map<String,Object> <span class="title">submitLogin</span><span class="params">(String username, String password,Boolean rememberMe,Model model)</span> </span>{</div><div class="line"> Map<String, Object> resultMap = <span class="keyword">new</span> LinkedHashMap<String, Object>();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> UsernamePasswordToken token = <span class="keyword">new</span> UsernamePasswordToken(username, password,rememberMe);</div><div class="line"> SecurityUtils.getSubject().login(token);</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">200</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"登录成功"</span>);</div><div class="line"></div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">500</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, e.getMessage());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>之前我是将shiro已经实现的UsernamePasswordToken类再封装了一层,最后发现没有必要,直接使用shiro提供的UsernamePasswordToken的类,其中有一个构造函数需要传rememberMe这个参数,也就是shiro为我们已经实现好了,推荐大家去看下UsernamePasswordToken这个类的源码。</p>
<ul>
<li>jsp页面的改造</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=utf-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"utf-8"</span>%></div><div class="line"><span class="tag"><<span class="name">%</span></span></div><div class="line"> <span class="attr">String</span> <span class="attr">path</span> = <span class="string">request.getContextPath();</span></div><div class="line"> <span class="attr">String</span> <span class="attr">basePath</span> = <span class="string">request.getScheme()</span> + "<span class="attr">:</span>//"</div><div class="line"> + <span class="attr">request.getServerName</span>() + "<span class="attr">:</span>" + <span class="attr">request.getServerPort</span>()</div><div class="line"> + <span class="attr">path</span>;</div><div class="line">%></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=utf-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span></span></div><div class="line"> <span class="attr">src</span>=<span class="string">"<%=basePath%>/static/js/jquery-1.11.3.js"</span>><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>登录<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> 错误信息:</div><div class="line"> <span class="tag"><<span class="name">h4</span> <span class="attr">id</span>=<span class="string">"erro"</span>></span><span class="tag"></<span class="name">h4</span>></span></div><div class="line"> <span class="tag"><<span class="name">form</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> 账号:<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"username"</span> <span class="attr">id</span>=<span class="string">"username"</span> <span class="attr">value</span>=<span class="string">"admin"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> 密码:<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">id</span>=<span class="string">"password"</span> <span class="attr">value</span>=<span class="string">"123"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span> </div><div class="line"> <span class="tag"><<span class="name">P</span>></span><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"checkbox"</span> <span class="attr">name</span>=<span class="string">"rememberMe"</span> <span class="attr">id</span>=<span class="string">"rememberMe"</span> /></span>记住我<span class="tag"></<span class="name">P</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">id</span>=<span class="string">"ajaxLogin"</span> <span class="attr">value</span>=<span class="string">"登录"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span></div><div class="line"> </div><div class="line"> <span class="tag"></<span class="name">form</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></div><div class="line">$(function(){</div><div class="line"> $("#ajaxLogin").click(function() {</div><div class="line"> var username = $("#username").val();</div><div class="line"> var password = $("#password").val();</div><div class="line"> var rememberMe =$('#rememberMe').is(':checked');</div><div class="line"> $.post("/ajaxLogin", {</div><div class="line"> "username" : username,</div><div class="line"> "password" : password,</div><div class="line"> "rememberMe" : rememberMe</div><div class="line"> }, function(result) {</div><div class="line"> if (result.status == 200) {</div><div class="line"> location.href = "/index";</div><div class="line"> } else {</div><div class="line"> $("#erro").html(result.message);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> });</div><div class="line">});</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<p>添加一个记住我的单选框,来控制是否需要记住我。</p>
<ul>
<li>修改权限配置,修改sys_permission_init表</li>
</ul>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170226191103113?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="链接权限控制表" title="">
</div>
<div class="image-caption">链接权限控制表</div>
</figure>
<p>因为getGifCode是获取验证码的链接,所以要配置为anon,不需要权限验证。user权限是配置记住我或认证通过可以访问,所以将/**链接设置为user权限,就可以实现记住我的功能。</p>
<p>注意权限添加的排序。</p>
<h2 id="GIF验证码"><a href="#GIF验证码" class="headerlink" title="GIF验证码"></a>GIF验证码</h2><ul>
<li>编写一个获取GIF验证码图片的controller:</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 获取验证码(Gif版本)</div><div class="line"> * <span class="doctag">@param</span> response</div><div class="line"> */</div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"getGifCode"</span>,method=RequestMethod.GET)</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getGifCode</span><span class="params">(HttpServletResponse response,HttpServletRequest request)</span></span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> response.setHeader(<span class="string">"Pragma"</span>, <span class="string">"No-cache"</span>); </div><div class="line"> response.setHeader(<span class="string">"Cache-Control"</span>, <span class="string">"no-cache"</span>); </div><div class="line"> response.setDateHeader(<span class="string">"Expires"</span>, <span class="number">0</span>); </div><div class="line"> response.setContentType(<span class="string">"image/gif"</span>); </div><div class="line"> <span class="comment">/**</span></div><div class="line"> * gif格式动画验证码</div><div class="line"> * 宽,高,位数。</div><div class="line"> */</div><div class="line"> Captcha captcha = <span class="keyword">new</span> GifCaptcha(<span class="number">146</span>,<span class="number">33</span>,<span class="number">4</span>);</div><div class="line"> <span class="comment">//输出</span></div><div class="line"> captcha.out(response.getOutputStream());</div><div class="line"> HttpSession session = request.getSession(<span class="keyword">true</span>); </div><div class="line"> <span class="comment">//存入Session</span></div><div class="line"> session.setAttribute(<span class="string">"_code"</span>,captcha.text().toLowerCase()); </div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> System.err.println(<span class="string">"获取验证码异常:"</span>+e.getMessage());</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>生成验证码后,将图片返回到页面,将字符串保存在当前会话的session域中。</p>
<p>这个GIF验证码的生成插件源码在我的项目io.z77z.vcode这个包下面,大家需要的话可以在我的码云上去下载。</p>
<ul>
<li>改造登录controller:</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * ajax登录请求</div><div class="line"> * <span class="doctag">@param</span> username</div><div class="line"> * <span class="doctag">@param</span> password</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"ajaxLogin"</span>,method=RequestMethod.POST)</div><div class="line"><span class="meta">@ResponseBody</span></div><div class="line"><span class="function"><span class="keyword">public</span> Map<String,Object> <span class="title">submitLogin</span><span class="params">(String username, String password,String vcode,Boolean rememberMe,Model model)</span> </span>{</div><div class="line"> Map<String, Object> resultMap = <span class="keyword">new</span> LinkedHashMap<String, Object>();</div><div class="line"> </div><div class="line"> <span class="keyword">if</span>(vcode==<span class="keyword">null</span>||vcode==<span class="string">""</span>){</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">500</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"验证码不能为空!"</span>);</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line"> }</div><div class="line"> </div><div class="line"> Session session = SecurityUtils.getSubject().getSession();</div><div class="line"> <span class="comment">//转化成小写字母</span></div><div class="line"> vcode = vcode.toLowerCase();</div><div class="line"> String v = (String) session.getAttribute(<span class="string">"_code"</span>);</div><div class="line"> <span class="comment">//还可以读取一次后把验证码清空,这样每次登录都必须获取验证码</span></div><div class="line"> <span class="comment">//session.removeAttribute("_come");</span></div><div class="line"> <span class="keyword">if</span>(!vcode.equals(v)){</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">500</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"验证码错误!"</span>);</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> UsernamePasswordToken token = <span class="keyword">new</span> UsernamePasswordToken(username, password,rememberMe);</div><div class="line"> SecurityUtils.getSubject().login(token);</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">200</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"登录成功"</span>);</div><div class="line"></div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">500</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, e.getMessage());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>登录的时候判断前台传入的验证码和session中的是否一致。</p>
<ul>
<li>前端jsp页面改造</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div></pre></td><td class="code"><pre><div class="line"><!DOCTYPE html></div><div class="line"><%@ page language="java" contentType="text/html; charset=utf-8"</div><div class="line"> pageEncoding="utf-8"%></div><div class="line"><%</div><div class="line"> String path = request.getContextPath();</div><div class="line"> String basePath = request.getScheme() + "://"</div><div class="line"> + request.getServerName() + ":" + request.getServerPort()</div><div class="line"> + path;</div><div class="line">%></div><div class="line"><html></div><div class="line"><head></div><div class="line"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></div><div class="line"><script type="text/javascript"</div><div class="line"> src="<%=basePath%>/static/js/jquery-1.11.3.js"></script></div><div class="line"><title>登录</title></div><div class="line"></head></div><div class="line"><body></div><div class="line"> 错误信息:</div><div class="line"> <h4 id="erro"></h4></div><div class="line"> <form></div><div class="line"> <p></div><div class="line"> 账号:<input type="text" name="username" id="username" value="admin" /></div><div class="line"> </p></div><div class="line"> <p></div><div class="line"> 密码:<input type="text" name="password" id="password" value="123" /></div><div class="line"> </p></div><div class="line"> <p></div><div class="line"> 验证码:<input type="text" name="vcode" id="vcode"/></div><div class="line"> </p></div><div class="line"> <p></div><div class="line"> <img alt="验证码" src="/getGifCode"></div><div class="line"> </p></div><div class="line"> </div><div class="line"> <P><input type="checkbox" name="rememberMe" id="rememberMe" />记住我</P></div><div class="line"> <p></div><div class="line"> <input type="button" id="ajaxLogin" value="登录" /></div><div class="line"> </p></div><div class="line"> </div><div class="line"> </form></div><div class="line"></body></div><div class="line"><script></div><div class="line">$(function(){</div><div class="line"> $("#ajaxLogin").click(function() {</div><div class="line"> var username = $("#username").val();</div><div class="line"> var password = $("#password").val();</div><div class="line"> var vcode = $("#vcode").val();</div><div class="line"> var rememberMe =$('#rememberMe').is(':checked');</div><div class="line"> $.post("/ajaxLogin", {</div><div class="line"> "username" : username,</div><div class="line"> "password" : password,</div><div class="line"> "vcode" : vcode,</div><div class="line"> "rememberMe" : rememberMe</div><div class="line"> }, function(result) {</div><div class="line"> if (result.status == 200) {</div><div class="line"> location.href = "/index";</div><div class="line"> } else {</div><div class="line"> $("#erro").html(result.message);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> });</div><div class="line">});</div><div class="line"></script></div><div class="line"></html></div></pre></td></tr></table></figure>
<h2 id="总结:"><a href="#总结:" class="headerlink" title="总结:"></a>总结:</h2><p>到此,我们集成shiro和redis,学习了一下功能的实现:</p>
<ol>
<li>用户必须要登陆之后才能访问定义链接,否则跳转到登录页面,被禁用户不能登录。并且对一些敏感操作链接设置权限,只有满足权限的才可以访问。</li>
<li>每个链接的权限信息保存在数据库,可以动态进行设置,并且热加载权限。</li>
<li>使用redis对shiro的用户信息进行缓存,不用每次都去执行MyShiroRealm.doGetAuthorizationInfo()权限认证方法。</li>
<li>之前有很多同学下载我的项目时,运行会报错,那是因为最近都在不断修改提交,有可能会出现版本问题,现在我在我的码云上面创建了stable_version分支,都是可以跑起来的。sqltable放在resource目录下面。</li>
<li>“记住我”的功能,利用cookie。</li>
<li>GIF验证码的生成,在登陆时进行验证码的校验。利用session。</li>
<li>下一博,我应该会写对在线用户的管理,踢出登录的功能学习记录。</li>
</ol>
<p>香蕉硬币点赞走一波啦。。。。。。</p>
]]></content>
<summary type="html">
<h2 id="学习目标"><a href="#学习目标" class="headerlink" title="学习目标"></a>学习目标</h2><p>如标题有如下两个功能实现:</p>
<ul>
<li><p>记住我的功能:通过设置key为“rememberMe”的cook
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="shiro" scheme="http://z77z.oschina.io/tags/shiro/"/>
<category term="GIF验证码" scheme="http://z77z.oschina.io/tags/GIF%E9%AA%8C%E8%AF%81%E7%A0%81/"/>
</entry>
<entry>
<title>SpringBoot+Shiro学习之数据库动态权限管理和Redis缓存</title>
<link href="http://z77z.oschina.io/2017/02/17/SpringBoot+Shiro%E5%AD%A6%E4%B9%A0%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8A%A8%E6%80%81%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E5%92%8CRedis%E7%BC%93%E5%AD%98/"/>
<id>http://z77z.oschina.io/2017/02/17/SpringBoot+Shiro学习之数据库动态权限管理和Redis缓存/</id>
<published>2017-02-17T14:12:58.000Z</published>
<updated>2017-02-26T10:42:40.106Z</updated>
<content type="html"><![CDATA[<h2 id="发现问题,需找解决思路。"><a href="#发现问题,需找解决思路。" class="headerlink" title="发现问题,需找解决思路。"></a>发现问题,需找解决思路。</h2><p>之前我们整合Shiro,完成了登录认证和权限管理的实现,登录认证没什么说的,需要实现AuthorizingRealm中的doGetAuthenticationInfo方法进行认证,但是我们在实现doGetAuthorizationInfo权限控制这个方法的时候发现以下两个问题:</p>
<ul>
<li><p>第一个问题:我们在ShiroConfig中配置链接权限的时候,每次只要有一个新的链接,或则权限需要改动,都要在ShiroConfig.java中进行权限的修改。而且改动后还需要重新启动程序新的权限才会生效,很麻烦。<strong>解决办法</strong>就是将这些链接的权限存入数据库,在前端可以提供增删改查的功能,在配置文件中编写权限的时候从数据库读取,当权限发生变更的时候利用ShiroFilterFactoryBean的清空功能,先clear,再set。这样就可以做到到动态的管理权限了。</p>
</li>
<li><p>第二个问题:每次在访问设置了权限的页面时,都会去执行doGetAuthorizationInfo方法来判断当前用户是否具备访问权限,由于在实际情况中,权限是不会经常改变的。<strong>解决办法</strong>就是进行缓存处理。</p>
</li>
</ul>
<p>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a></p>
<h2 id="第一个问题解决步骤"><a href="#第一个问题解决步骤" class="headerlink" title="第一个问题解决步骤"></a>第一个问题解决步骤</h2><p><strong>建立数据库</strong></p>
<p>我们从ShiroConfig中的<code>filterChainDefinitionMap.put("/add", "perms[权限添加]");</code> 配置可以看出,我们需要存储链接,和链接需要具备的权限这两个关键字段。还有这个权限的读取是有顺序的,所以还要进行排序控制,所以我新建表为:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">-- ----------------------------</div><div class="line">-- Table structure for sys_permission_init</div><div class="line">-- ----------------------------</div><div class="line">DROP TABLE IF EXISTS `sys_permission_init`;</div><div class="line">CREATE TABLE `sys_permission_init` (</div><div class="line"> `id` varchar(255) NOT NULL,</div><div class="line"> `url` varchar(255) DEFAULT NULL COMMENT '链接地址',</div><div class="line"> `permission_init` varchar(255) DEFAULT NULL COMMENT '需要具备的权限',</div><div class="line"> `sort` int(50) DEFAULT NULL COMMENT '排序',</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8;</div></pre></td></tr></table></figure>
<p><em>当然可以按实际情况进行表的设计,这里只做简单学习。</em></p>
<p><strong>改造ShiroConfig.java</strong></p>
<p>改造前:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span></div><div class="line"><span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shirFilter</span><span class="params">(SecurityManager securityManager)</span> </span>{</div><div class="line"> ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</div><div class="line"></div><div class="line"> <span class="comment">// 必须设置 SecurityManager</span></div><div class="line"> shiroFilterFactoryBean.setSecurityManager(securityManager);</div><div class="line"></div><div class="line"> <span class="comment">// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></div><div class="line"> shiroFilterFactoryBean.setLoginUrl(<span class="string">"/login"</span>);</div><div class="line"> <span class="comment">// 登录成功后要跳转的链接</span></div><div class="line"> shiroFilterFactoryBean.setSuccessUrl(<span class="string">"/index"</span>);</div><div class="line"> <span class="comment">// 未授权界面;</span></div><div class="line"> shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/403"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 拦截器.</span></div><div class="line"> Map<String, String> filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap<String, String>();</div><div class="line"> <span class="comment">// 配置不会被拦截的链接 顺序判断</span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/static/**"</span>, <span class="string">"anon"</span>);</div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/ajaxLogin"</span>, <span class="string">"anon"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了</span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/logout"</span>, <span class="string">"logout"</span>);</div><div class="line"></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/add"</span>, <span class="string">"perms[权限添加]"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;</span></div><div class="line"> <span class="comment">// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--></span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/**"</span>, <span class="string">"authc"</span>);</div><div class="line"></div><div class="line"> shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);</div><div class="line"> System.out.println(<span class="string">"Shiro拦截器工厂类注入成功"</span>);</div><div class="line"> <span class="keyword">return</span> shiroFilterFactoryBean;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>改造后:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span></div><div class="line"><span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shirFilter</span><span class="params">(SecurityManager securityManager)</span> </span>{</div><div class="line"> ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</div><div class="line"></div><div class="line"> <span class="comment">// 必须设置 SecurityManager</span></div><div class="line"> shiroFilterFactoryBean.setSecurityManager(securityManager);</div><div class="line"></div><div class="line"> <span class="comment">// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></div><div class="line"> shiroFilterFactoryBean.setLoginUrl(<span class="string">"/login"</span>);</div><div class="line"> <span class="comment">// 登录成功后要跳转的链接</span></div><div class="line"> shiroFilterFactoryBean.setSuccessUrl(<span class="string">"/index"</span>);</div><div class="line"> <span class="comment">// 未授权界面;</span></div><div class="line"> shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/403"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 权限控制map.</span></div><div class="line"> Map<String, String> filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap<String, String>();</div><div class="line"> <span class="comment">//从数据库获取</span></div><div class="line"> List<SysPermissionInit> list = sysPermissionInitService.selectAll();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (SysPermissionInit sysPermissionInit : list) {</div><div class="line"> filterChainDefinitionMap.put(sysPermissionInit.getUrl(),</div><div class="line"> sysPermissionInit.getPermissionInit());</div><div class="line"> }</div><div class="line"></div><div class="line"> shiroFilterFactoryBean</div><div class="line"> .setFilterChainDefinitionMap(filterChainDefinitionMap);</div><div class="line"> System.out.println(<span class="string">"Shiro拦截器工厂类注入成功"</span>);</div><div class="line"> <span class="keyword">return</span> shiroFilterFactoryBean;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这里的selectAll()就是从数据库查询之前创建的权限管理列表,这里就不贴具体的查询代码了。</p>
<p><strong>添加权限</strong></p>
<p>在数据库中添加权限如下图:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170216164400014?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title="">
</div>
<div class="image-caption">这里写图片描述</div>
</figure>
<p>现在启动程序,在控制台可以发现启动的时候程序在数据库查询了权限的列表信息。做到这步之后还没有达到动态的目的,比如现在到数据库手动修改/add链接的权限,这时不重启程序,权限是不会修改的。</p>
<p><strong>动态更改权限实现</strong></p>
<p>ShiroService.java:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * </div><div class="line"> * <span class="doctag">@author</span> 作者: z77z</div><div class="line"> * <span class="doctag">@date</span> 创建时间:2017年2月15日 下午4:16:07</div><div class="line"> */</div><div class="line"><span class="meta">@Service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroService</span> </span>{</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> ShiroFilterFactoryBean shiroFilterFactoryBean;</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> SysPermissionInitService sysPermissionInitService;</div><div class="line"> </div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 初始化权限</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> Map<String, String> <span class="title">loadFilterChainDefinitions</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">// 权限控制map.从数据库获取</span></div><div class="line"> Map<String, String> filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap<String, String>();</div><div class="line"> List<SysPermissionInit> list = sysPermissionInitService.selectAll();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (SysPermissionInit sysPermissionInit : list) {</div><div class="line"> filterChainDefinitionMap.put(sysPermissionInit.getUrl(),</div><div class="line"> sysPermissionInit.getPermissionInit());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> filterChainDefinitionMap;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 重新加载权限</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updatePermission</span><span class="params">()</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">synchronized</span> (shiroFilterFactoryBean) {</div><div class="line"></div><div class="line"> AbstractShiroFilter shiroFilter = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean</div><div class="line"> .getObject();</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(</div><div class="line"> <span class="string">"get ShiroFilter from shiroFilterFactoryBean error!"</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter</div><div class="line"> .getFilterChainResolver();</div><div class="line"> DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver</div><div class="line"> .getFilterChainManager();</div><div class="line"></div><div class="line"> <span class="comment">// 清空老的权限控制</span></div><div class="line"> manager.getFilterChains().clear();</div><div class="line"></div><div class="line"> shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();</div><div class="line"> shiroFilterFactoryBean</div><div class="line"> .setFilterChainDefinitionMap(loadFilterChainDefinitions());</div><div class="line"> <span class="comment">// 重新构建生成</span></div><div class="line"> Map<String, String> chains = shiroFilterFactoryBean</div><div class="line"> .getFilterChainDefinitionMap();</div><div class="line"> <span class="keyword">for</span> (Map.Entry<String, String> entry : chains.entrySet()) {</div><div class="line"> String url = entry.getKey();</div><div class="line"> String chainDefinition = entry.getValue().trim()</div><div class="line"> .replace(<span class="string">" "</span>, <span class="string">""</span>);</div><div class="line"> manager.createChain(url, chainDefinition);</div><div class="line"> }</div><div class="line"></div><div class="line"> System.out.println(<span class="string">"更新权限成功!!"</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这样,可以在修改权限之后,执行updatePermission()这个方法,权限就会先被clear,然后重新查询权限列表后再set。动态修改就实现了!</p>
<p>注意:在本学习项目里面,我在设置登录用户的权限的时候是写死了的,所以每个登录用户权限都是一样的,实际开发中在MyShiroRealm文件中设置登录用户的权限是从数据库获取的。还有在实际开发中sys_permission_init权限管理这种表是会在前端提供增删改查功能的,我学习的时候是直接在数据库手动修改。说到底,本人很懒!</p>
<h2 id="第二个问题的解决步骤"><a href="#第二个问题的解决步骤" class="headerlink" title="第二个问题的解决步骤"></a>第二个问题的解决步骤</h2><p>我们知道Shiro 提供了一系列让我们自己实现的接口,包括org.apache.shiro.cache.CacheManager 、org.apache.shiro.cache.Cache 等接口。那么我们要对这些做实现,就实现了 Shiro 对 Session 和用户认证信息、用户缓存信息等的缓存,存储。我们可以用缓存,如 Redis 、 memcache 、 EHCache 等,甚至我们可以用数据库,如 Oracle 、 Mysql 等,都可以,只有效率的快慢问题,功能都可以达到。</p>
<p>那么我的教程是采用了 Redis ,而且是用了Jedis 。Jedis 可以实现pool 和hash 的集群Redis 。</p>
<p>本来我想是在网上学习学习,自己实现redis的集成。最后发现已经有大神已经做了这个插件,对shiro提供的CacheManager,Cache ,这些接口使用redis都有了很好的实现。我就不需要再费心学习了,我们就直接拿来用。</p>
<p><strong>pom.xml依赖添加</strong></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- shiro+redis缓存插件 --></span></div><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.crazycake<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>shiro-redis<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.4.2.1-RELEASE<span class="tag"></<span class="name">version</span>></span></div><div class="line"><span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure>
<p><strong>改造ShiroConfig.java文件</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <span class="doctag">@author</span> 作者 z77z</div><div class="line"> * <span class="doctag">@date</span> 创建时间:2017年2月10日 下午1:16:38</div><div class="line"> * </div><div class="line"> */</div><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroConfig</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> SysPermissionInitService sysPermissionInitService;</div><div class="line"></div><div class="line"> <span class="meta">@Value</span>(<span class="string">"${spring.redis.host}"</span>)</div><div class="line"> <span class="keyword">private</span> String host;</div><div class="line"></div><div class="line"> <span class="meta">@Value</span>(<span class="string">"${spring.redis.port}"</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> port;</div><div class="line"> </div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shirFilter</span><span class="params">(SecurityManager securityManager)</span> </span>{</div><div class="line"> ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</div><div class="line"></div><div class="line"> <span class="comment">// 必须设置 SecurityManager</span></div><div class="line"> shiroFilterFactoryBean.setSecurityManager(securityManager);</div><div class="line"></div><div class="line"> <span class="comment">// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></div><div class="line"> shiroFilterFactoryBean.setLoginUrl(<span class="string">"/login"</span>);</div><div class="line"> <span class="comment">// 登录成功后要跳转的链接</span></div><div class="line"> shiroFilterFactoryBean.setSuccessUrl(<span class="string">"/index"</span>);</div><div class="line"> <span class="comment">// 未授权界面;</span></div><div class="line"> shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/403"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 权限控制map.</span></div><div class="line"> Map<String, String> filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap<String, String>();</div><div class="line"> <span class="comment">// 从数据库获取</span></div><div class="line"> List<SysPermissionInit> list = sysPermissionInitService.selectAll();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (SysPermissionInit sysPermissionInit : list) {</div><div class="line"> filterChainDefinitionMap.put(sysPermissionInit.getUrl(),</div><div class="line"> sysPermissionInit.getPermissionInit());</div><div class="line"> }</div><div class="line"></div><div class="line"> shiroFilterFactoryBean</div><div class="line"> .setFilterChainDefinitionMap(filterChainDefinitionMap);</div><div class="line"> System.out.println(<span class="string">"Shiro拦截器工厂类注入成功"</span>);</div><div class="line"> <span class="keyword">return</span> shiroFilterFactoryBean;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> SecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>{</div><div class="line"> DefaultWebSecurityManager securityManager = <span class="keyword">new</span> DefaultWebSecurityManager();</div><div class="line"> <span class="comment">// 设置realm.</span></div><div class="line"> securityManager.setRealm(myShiroRealm());</div><div class="line"> <span class="comment">// 自定义缓存实现 使用redis</span></div><div class="line"> securityManager.setCacheManager(cacheManager());</div><div class="line"> <span class="comment">// 自定义session管理 使用redis</span></div><div class="line"> securityManager.setSessionManager(SessionManager());</div><div class="line"> <span class="keyword">return</span> securityManager;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 身份认证realm; (这个需要自己写,账号密码校验;权限等)</div><div class="line"> * </div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> MyShiroRealm <span class="title">myShiroRealm</span><span class="params">()</span> </span>{</div><div class="line"> MyShiroRealm myShiroRealm = <span class="keyword">new</span> MyShiroRealm();</div><div class="line"> <span class="keyword">return</span> myShiroRealm;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 配置shiro redisManager</div><div class="line"> * </div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> RedisManager <span class="title">redisManager</span><span class="params">()</span> </span>{</div><div class="line"> RedisManager redisManager = <span class="keyword">new</span> RedisManager();</div><div class="line"> redisManager.setHost(host);</div><div class="line"> redisManager.setPort(port);</div><div class="line"> redisManager.setExpire(<span class="number">1800</span>);<span class="comment">// 配置过期时间</span></div><div class="line"> <span class="comment">// redisManager.setTimeout(timeout);</span></div><div class="line"> <span class="comment">// redisManager.setPassword(password);</span></div><div class="line"> <span class="keyword">return</span> redisManager;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * cacheManager 缓存 redis实现</div><div class="line"> * </div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> RedisCacheManager <span class="title">cacheManager</span><span class="params">()</span> </span>{</div><div class="line"> RedisCacheManager redisCacheManager = <span class="keyword">new</span> RedisCacheManager();</div><div class="line"> redisCacheManager.setRedisManager(redisManager());</div><div class="line"> <span class="keyword">return</span> redisCacheManager;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * RedisSessionDAO shiro sessionDao层的实现 通过redis</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> RedisSessionDAO <span class="title">redisSessionDAO</span><span class="params">()</span> </span>{</div><div class="line"> RedisSessionDAO redisSessionDAO = <span class="keyword">new</span> RedisSessionDAO();</div><div class="line"> redisSessionDAO.setRedisManager(redisManager());</div><div class="line"> <span class="keyword">return</span> redisSessionDAO;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * shiro session的管理</div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> DefaultWebSessionManager <span class="title">SessionManager</span><span class="params">()</span> </span>{</div><div class="line"> DefaultWebSessionManager sessionManager = <span class="keyword">new</span> DefaultWebSessionManager();</div><div class="line"> sessionManager.setSessionDAO(redisSessionDAO());</div><div class="line"> <span class="keyword">return</span> sessionManager;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这里,因为使用的是redis来做容器缓存,所以要创建redisManager来配置shiro,SessionManager(),cacheManager()这两个类都是插件给我们写好了的,里面就是对shiro提供的接口的redis实现方式。</p>
<p>使用插件就是这么简单,直接启动程序,多访问几次具有权限的页面,查看控制台发现,权限认证方法:MyShiroRealm.doGetAuthorizationInfo()会只执行了一次。说明我们的缓存生效了。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>到此,我们集成shiro和redis,学习了一下功能的实现:</p>
<ol>
<li>用户必须要登陆之后才能访问定义链接,否则跳转到登录页面,被禁用户不能登录。并且对一些敏感操作链接设置权限,只有满足权限的才可以访问。</li>
<li>每个链接的权限信息保存在数据库,可以动态进行设置,并且热加载权限。</li>
<li>使用redis对shiro的用户信息进行缓存,不用每次都去执行MyShiroRealm.doGetAuthorizationInfo()权限认证方法。</li>
<li>之前有很多同学下载我的项目时,运行会报错,那是因为最近都在不断修改提交,有可能会出现版本问题,现在我在我的码云上面创建了stable_version分支,都是可以跑起来的。sqltable放在resource目录下面。</li>
<li>下一博,我应该会写对在线用户的管理,踢出登录的功能学习记录。</li>
</ol>
<p>香蕉硬币点赞走一波啦。。。。。。</p>
]]></content>
<summary type="html">
<h2 id="发现问题,需找解决思路。"><a href="#发现问题,需找解决思路。" class="headerlink" title="发现问题,需找解决思路。"></a>发现问题,需找解决思路。</h2><p>之前我们整合Shiro,完成了登录认证和权限管理的实现,登录
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="redis" scheme="http://z77z.oschina.io/tags/redis/"/>
<category term="shiro" scheme="http://z77z.oschina.io/tags/shiro/"/>
</entry>
<entry>
<title>SpringBoot+shiro整合学习之登录认证和权限控制</title>
<link href="http://z77z.oschina.io/2017/02/13/SpringBoot+shiro%E6%95%B4%E5%90%88%E5%AD%A6%E4%B9%A0%E4%B9%8B%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E5%92%8C%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6/"/>
<id>http://z77z.oschina.io/2017/02/13/SpringBoot+shiro整合学习之登录认证和权限控制/</id>
<published>2017-02-13T14:46:58.000Z</published>
<updated>2017-02-13T14:46:28.172Z</updated>
<content type="html"><![CDATA[<h2 id="学习任务目标"><a href="#学习任务目标" class="headerlink" title="学习任务目标"></a>学习任务目标</h2><ol>
<li><p>用户必须要登陆之后才能访问定义链接,否则跳转到登录页面。</p>
</li>
<li><p>对链接进行权限控制,只有当当前登录用户有这个链接访问权限才可以访问,否则跳转到指定页面。</p>
</li>
<li><p>输入错误密码用户名或则用户被设置为静止登录,返回相应json串信息。</p>
</li>
</ol>
<blockquote>
<p>我是用的是之前搭建的一个springboot+mybatisplus+jsp的一个基础框架。在这之上进行shiro的整合。需要的同学可以去我的码云下载。</p>
</blockquote>
<p>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a></p>
<h2 id="导入shiro依赖包到pom-xml"><a href="#导入shiro依赖包到pom-xml" class="headerlink" title="导入shiro依赖包到pom.xml"></a>导入shiro依赖包到pom.xml</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- shiro权限控制框架 --></span></div><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.shiro<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>shiro-spring<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.3.2<span class="tag"></<span class="name">version</span>></span></div><div class="line"><span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure>
<h2 id="采用RBAC模式建立数据库"><a href="#采用RBAC模式建立数据库" class="headerlink" title="采用RBAC模式建立数据库"></a>采用RBAC模式建立数据库</h2><blockquote>
<p>RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。</p>
</blockquote>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line">/*表结构插入*/</div><div class="line">DROP TABLE IF EXISTS `u_permission`;</div><div class="line"></div><div class="line">CREATE TABLE `u_permission` (</div><div class="line"> `id` bigint(20) NOT NULL AUTO_INCREMENT,</div><div class="line"> `url` varchar(256) DEFAULT NULL COMMENT 'url地址',</div><div class="line"> `name` varchar(64) DEFAULT NULL COMMENT 'url描述',</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;</div><div class="line"></div><div class="line">/*Table structure for table `u_role` */</div><div class="line"></div><div class="line">DROP TABLE IF EXISTS `u_role`;</div><div class="line"></div><div class="line">CREATE TABLE `u_role` (</div><div class="line"> `id` bigint(20) NOT NULL AUTO_INCREMENT,</div><div class="line"> `name` varchar(32) DEFAULT NULL COMMENT '角色名称',</div><div class="line"> `type` varchar(10) DEFAULT NULL COMMENT '角色类型',</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;</div><div class="line"></div><div class="line">/*Table structure for table `u_role_permission` */</div><div class="line"></div><div class="line">DROP TABLE IF EXISTS `u_role_permission`;</div><div class="line"></div><div class="line">CREATE TABLE `u_role_permission` (</div><div class="line"> `rid` bigint(20) DEFAULT NULL COMMENT '角色ID',</div><div class="line"> `pid` bigint(20) DEFAULT NULL COMMENT '权限ID'</div><div class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8;</div><div class="line"></div><div class="line">/*Table structure for table `u_user` */</div><div class="line"></div><div class="line">DROP TABLE IF EXISTS `u_user`;</div><div class="line"></div><div class="line">CREATE TABLE `u_user` (</div><div class="line"> `id` bigint(20) NOT NULL AUTO_INCREMENT,</div><div class="line"> `nickname` varchar(20) DEFAULT NULL COMMENT '用户昵称',</div><div class="line"> `email` varchar(128) DEFAULT NULL COMMENT '邮箱|登录帐号',</div><div class="line"> `pswd` varchar(32) DEFAULT NULL COMMENT '密码',</div><div class="line"> `create_time` datetime DEFAULT NULL COMMENT '创建时间',</div><div class="line"> `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',</div><div class="line"> `status` bigint(1) DEFAULT '1' COMMENT '1:有效,0:禁止登录',</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;</div><div class="line"></div><div class="line">/*Table structure for table `u_user_role` */</div><div class="line"></div><div class="line">DROP TABLE IF EXISTS `u_user_role`;</div><div class="line"></div><div class="line">CREATE TABLE `u_user_role` (</div><div class="line"> `uid` bigint(20) DEFAULT NULL COMMENT '用户ID',</div><div class="line"> `rid` bigint(20) DEFAULT NULL COMMENT '角色ID'</div><div class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8;</div></pre></td></tr></table></figure>
<h2 id="Dao层代码的编写"><a href="#Dao层代码的编写" class="headerlink" title="Dao层代码的编写"></a>Dao层代码的编写</h2><blockquote>
<p>Dao层的entity,service,mapper等我是采用mybatisplus的代码自动生成工具生成的,具备了单表的增删改查功能和分页功能,比较方便,这里我就不贴代码了。</p>
</blockquote>
<h2 id="配置shiro"><a href="#配置shiro" class="headerlink" title="配置shiro"></a>配置shiro</h2><p><strong>ShiroConfig.java</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <span class="doctag">@author</span> 作者 z77z</div><div class="line"> * <span class="doctag">@date</span> 创建时间:2017年2月10日 下午1:16:38</div><div class="line"> * </div><div class="line"> */</div><div class="line"><span class="meta">@Configuration</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShiroConfig</span> </span>{</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * ShiroFilterFactoryBean 处理拦截资源文件问题。</div><div class="line"> * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在</div><div class="line"> * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager</div><div class="line"> *</div><div class="line"> * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过</div><div class="line"> * 3、部分过滤器可指定参数,如perms,roles</div><div class="line"> *</div><div class="line"> */</div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> ShiroFilterFactoryBean <span class="title">shirFilter</span><span class="params">(SecurityManager securityManager)</span> </span>{</div><div class="line"> ShiroFilterFactoryBean shiroFilterFactoryBean = <span class="keyword">new</span> ShiroFilterFactoryBean();</div><div class="line"></div><div class="line"> <span class="comment">// 必须设置 SecurityManager</span></div><div class="line"> shiroFilterFactoryBean.setSecurityManager(securityManager);</div><div class="line"></div><div class="line"> <span class="comment">// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面</span></div><div class="line"> shiroFilterFactoryBean.setLoginUrl(<span class="string">"/login"</span>);</div><div class="line"> <span class="comment">// 登录成功后要跳转的链接</span></div><div class="line"> shiroFilterFactoryBean.setSuccessUrl(<span class="string">"/index"</span>);</div><div class="line"> <span class="comment">// 未授权界面;</span></div><div class="line"> shiroFilterFactoryBean.setUnauthorizedUrl(<span class="string">"/403"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 拦截器.</span></div><div class="line"> Map<String, String> filterChainDefinitionMap = <span class="keyword">new</span> LinkedHashMap<String, String>();</div><div class="line"> <span class="comment">// 配置不会被拦截的链接 顺序判断</span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/static/**"</span>, <span class="string">"anon"</span>);</div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/ajaxLogin"</span>, <span class="string">"anon"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了</span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/logout"</span>, <span class="string">"logout"</span>);</div><div class="line"></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/add"</span>, <span class="string">"perms[权限添加]"</span>);</div><div class="line"></div><div class="line"> <span class="comment">// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;</span></div><div class="line"> <span class="comment">// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--></span></div><div class="line"> filterChainDefinitionMap.put(<span class="string">"/**"</span>, <span class="string">"authc"</span>);</div><div class="line"></div><div class="line"> shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);</div><div class="line"> System.out.println(<span class="string">"Shiro拦截器工厂类注入成功"</span>);</div><div class="line"> <span class="keyword">return</span> shiroFilterFactoryBean;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> SecurityManager <span class="title">securityManager</span><span class="params">()</span> </span>{</div><div class="line"> DefaultWebSecurityManager securityManager = <span class="keyword">new</span> DefaultWebSecurityManager();</div><div class="line"> <span class="comment">// 设置realm.</span></div><div class="line"> securityManager.setRealm(myShiroRealm());</div><div class="line"> <span class="keyword">return</span> securityManager;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 身份认证realm; (这个需要自己写,账号密码校验;权限等)</div><div class="line"> * </div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="meta">@Bean</span></div><div class="line"> <span class="function"><span class="keyword">public</span> MyShiroRealm <span class="title">myShiroRealm</span><span class="params">()</span> </span>{</div><div class="line"> MyShiroRealm myShiroRealm = <span class="keyword">new</span> MyShiroRealm();</div><div class="line"> <span class="keyword">return</span> myShiroRealm;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="登录认证实现"><a href="#登录认证实现" class="headerlink" title="登录认证实现"></a>登录认证实现</h2><blockquote>
<p>在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO.</p>
<p>Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法。<br>该方法主要执行以下操作:</p>
<p>1、检查提交的进行认证的令牌信息</p>
<p>2、根据令牌信息从数据源(通常为数据库)中获取用户信息</p>
<p>3、对用户信息进行匹配验证。</p>
<p>4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。</p>
<p>5、验证失败则抛出AuthenticationException异常信息。</p>
<p>而在我们的应用程序中要做的就是自定义一个Realm类,继承AuthorizingRealm抽象类,重载doGetAuthenticationInfo<br>(),重写获取用户信息的方法。</p>
</blockquote>
<p><strong>doGetAuthenticationInfo的重写</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 认证信息.(身份验证) : Authentication 是用来验证用户身份</div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> token</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> * <span class="doctag">@throws</span> AuthenticationException</div><div class="line"> */</div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">protected</span> AuthenticationInfo <span class="title">doGetAuthenticationInfo</span><span class="params">(</span></span></div><div class="line"> AuthenticationToken authcToken) <span class="keyword">throws</span> AuthenticationException {</div><div class="line"> System.out.println(<span class="string">"身份认证方法:MyShiroRealm.doGetAuthenticationInfo()"</span>);</div><div class="line"></div><div class="line"> ShiroToken token = (ShiroToken) authcToken;</div><div class="line"> Map<String, Object> map = <span class="keyword">new</span> HashMap<String, Object>();</div><div class="line"> map.put(<span class="string">"nickname"</span>, token.getUsername());</div><div class="line"> map.put(<span class="string">"pswd"</span>, token.getPswd());</div><div class="line"> SysUser user = <span class="keyword">null</span>;</div><div class="line"> <span class="comment">// 从数据库获取对应用户名密码的用户</span></div><div class="line"> List<SysUser> userList = sysUserService.selectByMap(map);</div><div class="line"> <span class="keyword">if</span>(userList.size()!=<span class="number">0</span>){</div><div class="line"> user = userList.get(<span class="number">0</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">null</span> == user) {</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> AccountException(<span class="string">"帐号或密码不正确!"</span>);</div><div class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(user.getStatus()==<span class="number">0</span>){</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 如果用户的status为禁用。那么就抛出<code>DisabledAccountException</code></div><div class="line"> */</div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> DisabledAccountException(<span class="string">"帐号已经禁止登录!"</span>);</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> <span class="comment">//更新登录时间 last login time</span></div><div class="line"> user.setLastLoginTime(<span class="keyword">new</span> Date());</div><div class="line"> sysUserService.updateById(user);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> SimpleAuthenticationInfo(user, user.getPswd(), getName());</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>通俗的说,这个的重写就是我们第一个学习目标的实现。</p>
</blockquote>
<h2 id="链接权限的实现"><a href="#链接权限的实现" class="headerlink" title="链接权限的实现"></a>链接权限的实现</h2><blockquote>
<p>shiro的权限授权是通过继承AuthorizingRealm抽象类,重载doGetAuthorizationInfo();</p>
<p>当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。</p>
<p>在这个方法中主要是使用类:SimpleAuthorizationInfo</p>
<p>进行角色的添加和权限的添加。</p>
<p>authorizationInfo.addRole(role.getRole());</p>
<p>authorizationInfo.addStringPermission(p.getPermission());</p>
<p>当然也可以添加set集合:roles是从数据库查询的当前用户的角色,stringPermissions是从数据库查询的当前用户对应的权限</p>
<p>authorizationInfo.setRoles(roles);</p>
<p>authorizationInfo.setStringPermissions(stringPermissions);</p>
<p>就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “perms[权限添加]”);<br>就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问,</p>
<p>如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “roles[100002],perms[权限添加]”);<br>就说明访问/add这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">* 授权</div><div class="line"> */</div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">protected</span> AuthorizationInfo <span class="title">doGetAuthorizationInfo</span><span class="params">(</span></span></div><div class="line"> PrincipalCollection principals) {</div><div class="line"> System.out.println(<span class="string">"权限认证方法:MyShiroRealm.doGetAuthenticationInfo()"</span>);</div><div class="line"> SysUser token = (SysUser)SecurityUtils.getSubject().getPrincipal();</div><div class="line"> String userId = token.getId();</div><div class="line"> SimpleAuthorizationInfo info = <span class="keyword">new</span> SimpleAuthorizationInfo();</div><div class="line"> <span class="comment">//根据用户ID查询角色(role),放入到Authorization里。</span></div><div class="line"> <span class="comment">/*Map<String, Object> map = new HashMap<String, Object>();</span></div><div class="line"> map.put("user_id", userId);</div><div class="line"> List<SysRole> roleList = sysRoleService.selectByMap(map);</div><div class="line"> Set<String> roleSet = new HashSet<String>();</div><div class="line"> for(SysRole role : roleList){</div><div class="line"> roleSet.add(role.getType());</div><div class="line"> }*/</div><div class="line"> <span class="comment">//实际开发,当前登录用户的角色和权限信息是从数据库来获取的,我这里写死是为了方便测试</span></div><div class="line"> Set<String> roleSet = <span class="keyword">new</span> HashSet<String>();</div><div class="line"> roleSet.add(<span class="string">"100002"</span>);</div><div class="line"> info.setRoles(roleSet);</div><div class="line"> <span class="comment">//根据用户ID查询权限(permission),放入到Authorization里。</span></div><div class="line"> <span class="comment">/*List<SysPermission> permissionList = sysPermissionService.selectByMap(map);</span></div><div class="line"> Set<String> permissionSet = new HashSet<String>();</div><div class="line"> for(SysPermission Permission : permissionList){</div><div class="line"> permissionSet.add(Permission.getName());</div><div class="line"> }*/</div><div class="line"> Set<String> permissionSet = <span class="keyword">new</span> HashSet<String>();</div><div class="line"> permissionSet.add(<span class="string">"权限添加"</span>);</div><div class="line"> info.setStringPermissions(permissionSet);</div><div class="line"> <span class="keyword">return</span> info;</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>这个类的实现是完成了我们学习目标的第二个任务。</p>
</blockquote>
<h2 id="编写web层的代码"><a href="#编写web层的代码" class="headerlink" title="编写web层的代码"></a>编写web层的代码</h2><p><strong>登录页面:</strong></p>
<p><strong>controller</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//跳转到登录表单页面</span></div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"login"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">login</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="string">"login"</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * ajax登录请求</div><div class="line"> * <span class="doctag">@param</span> username</div><div class="line"> * <span class="doctag">@param</span> password</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"ajaxLogin"</span>,method=RequestMethod.POST)</div><div class="line"><span class="meta">@ResponseBody</span></div><div class="line"><span class="function"><span class="keyword">public</span> Map<String,Object> <span class="title">submitLogin</span><span class="params">(String username, String password,Model model)</span> </span>{</div><div class="line"> Map<String, Object> resultMap = <span class="keyword">new</span> LinkedHashMap<String, Object>();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> </div><div class="line"> ShiroToken token = <span class="keyword">new</span> ShiroToken(username, password);</div><div class="line"> SecurityUtils.getSubject().login(token);</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">200</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, <span class="string">"登录成功"</span>);</div><div class="line"></div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> resultMap.put(<span class="string">"status"</span>, <span class="number">500</span>);</div><div class="line"> resultMap.put(<span class="string">"message"</span>, e.getMessage());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>jsp</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=utf-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"utf-8"</span>%></div><div class="line"><span class="tag"><<span class="name">%</span></span></div><div class="line"> <span class="attr">String</span> <span class="attr">path</span> = <span class="string">request.getContextPath();</span></div><div class="line"> <span class="attr">String</span> <span class="attr">basePath</span> = <span class="string">request.getScheme()</span> + "<span class="attr">:</span>//"</div><div class="line"> + <span class="attr">request.getServerName</span>() + "<span class="attr">:</span>" + <span class="attr">request.getServerPort</span>()</div><div class="line"> + <span class="attr">path</span>;</div><div class="line">%></div><div class="line"><span class="meta"><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"></span></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=utf-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span></span></div><div class="line"> <span class="attr">src</span>=<span class="string">"<%=basePath%>/static/js/jquery-1.11.3.js"</span>><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>登录<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> 错误信息:</div><div class="line"> <span class="tag"><<span class="name">h4</span> <span class="attr">id</span>=<span class="string">"erro"</span>></span><span class="tag"></<span class="name">h4</span>></span></div><div class="line"> <span class="tag"><<span class="name">form</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> 账号:<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"username"</span> <span class="attr">id</span>=<span class="string">"username"</span> <span class="attr">value</span>=<span class="string">"admin"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> 密码:<span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">id</span>=<span class="string">"password"</span> <span class="attr">value</span>=<span class="string">"123"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">id</span>=<span class="string">"ajaxLogin"</span> <span class="attr">value</span>=<span class="string">"登录"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"></<span class="name">form</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></div><div class="line"> var username = $("#username").val();</div><div class="line"> var password = $("#password").val();</div><div class="line"> $("#ajaxLogin").click(function() {</div><div class="line"> $.post("/ajaxLogin", {</div><div class="line"> "username" : username,</div><div class="line"> "password" : password</div><div class="line"> }, function(result) {</div><div class="line"> if (result.status == 200) {</div><div class="line"> location.href = "/index";</div><div class="line"> } else {</div><div class="line"> $("#erro").html(result.message);</div><div class="line"> }</div><div class="line"> });</div><div class="line"> });</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<h2 id="主页页面"><a href="#主页页面" class="headerlink" title="主页页面"></a>主页页面</h2><p><strong>controller</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//跳转到主页</span></div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"index"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">index</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="string">"index"</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line">* 退出</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"logout"</span>,method =RequestMethod.GET)</div><div class="line"><span class="meta">@ResponseBody</span></div><div class="line"><span class="function"><span class="keyword">public</span> Map<String,Object> <span class="title">logout</span><span class="params">()</span></span>{</div><div class="line"> Map<String, Object> resultMap = <span class="keyword">new</span> LinkedHashMap<String, Object>();</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">//退出</span></div><div class="line"> SecurityUtils.getSubject().logout();</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> System.err.println(e.getMessage());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> resultMap;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>jsp</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=UTF-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"UTF-8"</span>%></div><div class="line"><span class="tag"><<span class="name">%</span></span></div><div class="line"> <span class="attr">String</span> <span class="attr">path</span> = <span class="string">request.getContextPath();</span></div><div class="line"> <span class="attr">String</span> <span class="attr">basePath</span> = <span class="string">request.getScheme()</span> + "<span class="attr">:</span>//"</div><div class="line"> + <span class="attr">request.getServerName</span>() + "<span class="attr">:</span>" + <span class="attr">request.getServerPort</span>()</div><div class="line"> + <span class="attr">path</span>;</div><div class="line">%></div><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=UTF-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span></span></div><div class="line"> <span class="attr">src</span>=<span class="string">"<%=basePath%>/static/js/jquery-1.11.3.js"</span>><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>Insert title here<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> helloJsp</div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">id</span>=<span class="string">"logout"</span> <span class="attr">value</span>=<span class="string">"退出登录"</span> /></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="undefined"></span></div><div class="line"> $("#logout").click(function(){</div><div class="line"> location.href="/logout";</div><div class="line"> });</div><div class="line"><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<p><strong>添加操作页面</strong></p>
<p><strong>controller</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@RequestMapping</span>(value=<span class="string">"add"</span>)</div><div class="line"><span class="function"><span class="keyword">public</span> String <span class="title">add</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="string">"add"</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>jsp</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=UTF-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"UTF-8"</span>%></div><div class="line"><span class="tag"><<span class="name">%</span></span></div><div class="line"> <span class="attr">String</span> <span class="attr">path</span> = <span class="string">request.getContextPath();</span></div><div class="line"> <span class="attr">String</span> <span class="attr">basePath</span> = <span class="string">request.getScheme()</span> + "<span class="attr">:</span>//"</div><div class="line"> + <span class="attr">request.getServerName</span>() + "<span class="attr">:</span>" + <span class="attr">request.getServerPort</span>()</div><div class="line"> + <span class="attr">path</span>;</div><div class="line">%></div><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=UTF-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span></span></div><div class="line"> <span class="attr">src</span>=<span class="string">"<%=basePath%>/static/js/jquery-1.11.3.js"</span>><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>Insert title here<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line">具有添加权限</div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p><strong>任务一</strong></p>
<blockquote>
<p>编写好后就可以启动程序,访问index页面,由于没有登录就会跳转到login页面。</p>
<p>登录之后就会跳转到index页面,点击退出登录后,有直接在浏览器中输入index页面访问,又会跳转到login页面</p>
<p>上面这些操作时候触发MyShiroRealm.doGetAuthenticationInfo()这个方法,也就是登录认证的方法。</p>
</blockquote>
<hr>
<p><strong>任务二</strong></p>
<blockquote>
<p>登录之后访问add页面成功访问,在shiro配置文件中改变add的访问权限为</p>
<p>filterChainDefinitionMap.put(“/add”,”perms[权限删除]”);</p>
<p>再重新启动程序,登录后访问,会重定向到/403页面,由于没有编写403页面,报404错误。</p>
<p>上面这些操作,会触发权限认证方法:MyShiroRealm.doGetAuthorizationInfo(),每访问一次就会触发一次。</p>
</blockquote>
<hr>
<p><strong>任务三</strong></p>
<blockquote>
<p>输入错误的用户名或则密码,返回“帐号或密码不正确!”的错误信息,在数据库中把一个用户的状态改为被禁用,再登陆,提示“帐号已经禁止登录!”的错误信息</p>
<p>上面的操作,是在MyShiroRealm.doGetAuthenticationInfo()登录认证的方法中实现的,通过查询数据库判断当前登录用户是否被禁用,具体可以去看源码。</p>
</blockquote>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>当然shiro很强大,这仅仅是完成了登录认证和权限管理这两个功能,接下来我会继续学习和分享,说说接下来的学习路线吧:</p>
<ol>
<li><p>shiro+redis集成,避免每次访问有权限的链接都会去执行MyShiroRealm.doGetAuthenticationInfo()方法来查询当前用户的权限,因为实际情况中权限是不会经常变得,这样就可以使用redis进行权限的缓存。</p>
</li>
<li><p>实现shiro链接权限的动态加载,之前要添加一个链接的权限,要在shiro的配置文件中添加filterChainDefinitionMap.put(“/add”, “roles[100002],perms[权限添加]”),这样很不方便管理,一种方法是将链接的权限使用数据库进行加载,另一种是通过init配置文件的方式读取。</p>
</li>
<li><p>Shiro 自定义权限校验Filter定义,及功能实现。</p>
</li>
<li><p>Shiro Ajax请求权限不满足,拦截后解决方案。这里有一个前提,我们知道Ajax不能做页面redirect和forward跳转,所以Ajax请求假如没登录,那么这个请求给用户的感觉就是没有任何反应,而用户又不知道用户已经退出了。</p>
</li>
<li><p>Shiro JSP标签使用。</p>
</li>
<li><p>Shiro 登录后跳转到最后一个访问的页面</p>
</li>
<li><p>在线显示,在线用户管理(踢出登录)。</p>
</li>
<li><p>登录注册密码加密传输。</p>
</li>
<li><p>集成验证码。</p>
</li>
<li><p>记住我的功能。关闭浏览器后还是登录状态。</p>
</li>
<li><p>还有没有想到的后面再说,欢迎大家提出一些建议。</p>
</li>
</ol>
]]></content>
<summary type="html">
<h2 id="学习任务目标"><a href="#学习任务目标" class="headerlink" title="学习任务目标"></a>学习任务目标</h2><ol>
<li><p>用户必须要登陆之后才能访问定义链接,否则跳转到登录页面。</p>
</li>
<li><p
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="shiro" scheme="http://z77z.oschina.io/tags/shiro/"/>
</entry>
<entry>
<title>SB集成Redis学习笔记之实际应用场景-java干货</title>
<link href="http://z77z.oschina.io/2017/02/06/SB%E9%9B%86%E6%88%90Redis%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8B%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF-java%E5%B9%B2%E8%B4%A7/"/>
<id>http://z77z.oschina.io/2017/02/06/SB集成Redis学习笔记之实际应用场景-java干货/</id>
<published>2017-02-06T13:22:58.000Z</published>
<updated>2017-02-06T13:29:03.430Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>之前利用springBoot中的缓存机制,使用Redis作为缓存容器,做了一个缓存的简单Demo,当然Redis不仅仅可以用来做缓存的容器,还有很多开发的实际场景中会用到Redis的特性,通过几天的学习,现将学习的成果整理分享给大家。希望大家也多多讨论,提供跟多的使用场景,来熟练掌握在springboot中的使用。</p>
</blockquote>
<p>个人博客:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></p>
<p>此项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a></p>
<h1 id="Redis的特性"><a href="#Redis的特性" class="headerlink" title="Redis的特性"></a>Redis的特性</h1><ul>
<li><p><strong>Strings</strong>:Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字.<br>常用命令: set,get,decr,incr,mget 等。<br>常用方法:</p>
<ol>
<li>获取字符串长度</li>
<li>往字符串append内容</li>
<li>设置和获取字符串的某一段内容</li>
<li>设置及获取字符串的某一位(bit)</li>
<li>批量设置一系列字符串的内容</li>
</ol>
</li>
<li><p><strong>Hashs</strong>:Redis Hash对应Value内部实际就是一个HashMap,常用命令:hget,hset,hgetall 等。</p>
</li>
<li><p><strong>Lists</strong>:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。<br>常用命令:lpush,rpush,lpop,rpop,lrange等。</p>
</li>
<li><p><strong>Sets</strong>:Sets 集合的概念就是一堆不重复值的组合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能。<br>常用命令:sadd,spop,smembers,sunion 等。</p>
</li>
<li><p><strong>Sorted Sets</strong>:Redis sorted set的使用场景与set类似,区别是set不是自动有序的。sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。<br>常用命令:zadd,zrange,zrem,zcard等</p>
</li>
</ul>
<blockquote>
<p>下面就根据这几个特性,也就是Redis支持的数据类型,来完成以下场景的实现。</p>
</blockquote>
<h1 id="场景一:简单计数功能"><a href="#场景一:简单计数功能" class="headerlink" title="场景一:简单计数功能"></a>场景一:简单计数功能</h1><blockquote>
<p>Redis是一个很好的计数器,计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。使用场景比如网站的访问数,注册用户数,文章的点赞数,高并发的秒杀活动,分布式序列号生成等等统计计数的功能实现。Redis 解决这类计数问题得心应手,相比关系数据库速度更快,消耗资源更少。还可以通过set()方法来重置计数。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 简单计数</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test1</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();</div><div class="line"> <span class="comment">//计数前打印</span></div><div class="line"> System.out.println(opsForValue.get(<span class="string">"test1"</span>));</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> <span class="comment">//计数,第一个参数为key值,第二个参数为每次增加计数的单位</span></div><div class="line"> opsForValue.increment(<span class="string">"test1"</span>, <span class="number">1</span>);</div><div class="line"> }</div><div class="line"> <span class="comment">//计数后打印</span></div><div class="line"> System.out.println(opsForValue.get(<span class="string">"test1"</span>));</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景二:按时间统计计数"><a href="#场景二:按时间统计计数" class="headerlink" title="场景二:按时间统计计数"></a>场景二:按时间统计计数</h1><blockquote>
<p>有时候除了简单的计数外,比如注册用户数需要按日统计,处理方法比较简单,把日期带入计数器 key 就可以。以此类推,还可以按其他方式进行统计计数,只需要把统计的方式添加到key值就可以了</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 按时间计数 </span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test2</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">//将日期添加到key值中</span></div><div class="line"> String key = <span class="string">"test2_"</span> + <span class="keyword">new</span> SimpleDateFormat(<span class="string">"yyyy-MM-dd"</span>).format(<span class="keyword">new</span> Date());</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();</div><div class="line"> System.out.println(opsForValue.get(key));</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> opsForValue.increment(key, <span class="number">1</span>);</div><div class="line"> }</div><div class="line"> System.out.println(opsForValue.get(key));</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景三:按模糊Key值查询"><a href="#场景三:按模糊Key值查询" class="headerlink" title="场景三:按模糊Key值查询"></a>场景三:按模糊Key值查询</h1><blockquote>
<p>在按条件统计计数的时候,把时间加入到了key值中,有时候要只是查询某个对象的统计数时,就可以使用模糊Key值查询。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 模糊K值查询</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test3</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();</div><div class="line"> <span class="comment">//先获取前缀为test的Key值列表。</span></div><div class="line"> Set<String> keys = stringRedisTemplate.keys(<span class="string">"test*"</span>);</div><div class="line"> <span class="comment">//遍历满足条件的Key值获取对应的value值</span></div><div class="line"> <span class="keyword">for</span> (String a : keys) {</div><div class="line"> System.out.println(a + <span class="string">":"</span> + opsForValue.get(a));</div><div class="line"> }</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>场景四:设置Key的有效时间(防止高并发访问)</p>
<blockquote>
<p>在redis中可以设置key值的有效时间,用户访问链接的时候,将用户的唯一信息比如ip地址等为key值,时间为value值,在redis中记录一下,在用户再次访问的时候,通过key值获取前一次访问的时间,比较时间的间隔,如果低于阀值,就拒绝这次请求,防止用户多次访问。这里只是写下在spring的RedisTemplate接口怎么使用。具体的逻辑实现自己搞定。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 设置key值的有效时间</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test4</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();</div><div class="line"> opsForValue.set(<span class="string">"test4"</span>, <span class="string">"test4"</span>);</div><div class="line"> System.out.println(opsForValue.get(<span class="string">"test4"</span>));</div><div class="line"> <span class="comment">// TimeUnit.SECONDS:解释定时参数的单位</span></div><div class="line"> <span class="comment">// MICROSECONDS 微秒 一百万分之一秒(就是毫秒/1000)</span></div><div class="line"> <span class="comment">// MILLISECONDS 毫秒 千分之一秒</span></div><div class="line"> <span class="comment">// NANOSECONDS 毫微秒 十亿分之一秒(就是微秒/1000)</span></div><div class="line"> <span class="comment">// SECONDS 秒</span></div><div class="line"> <span class="comment">// MINUTES 分钟</span></div><div class="line"> <span class="comment">// HOURS 小时</span></div><div class="line"> <span class="comment">// DAYS 天</span></div><div class="line"> <span class="keyword">if</span>(stringRedisTemplate.expire(<span class="string">"test4"</span>, <span class="number">5</span>, TimeUnit.SECONDS)){</div><div class="line"> System.out.println(<span class="string">"设置过期时间成功,等待。。。。"</span>);</div><div class="line"> Thread.sleep(<span class="number">5001</span>);</div><div class="line"> }</div><div class="line"> System.out.println(opsForValue.get(<span class="string">"test4"</span>));</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景五:使用hashs存储获取修改java对象"><a href="#场景五:使用hashs存储获取修改java对象" class="headerlink" title="场景五:使用hashs存储获取修改java对象"></a>场景五:使用hashs存储获取修改java对象</h1><blockquote>
<p>在实际开发中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。</p>
<p>而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。因为Redis的Hash结构是以对象的名字作为redis的key值,以对象的唯一属性值作为hash的key值,以对象来作为redis的value值。结构图如下:</p>
</blockquote>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170206205428032?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="hashs存储结构图" title="">
</div>
<div class="image-caption">hashs存储结构图</div>
</figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 使用hashs存储获取对象</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test5</span><span class="params">()</span></span>{</div><div class="line"> BeautifulPictures beautifulPictures = beautifulPicturesService.selectById(<span class="number">1</span>);</div><div class="line"> HashOperations<String, Object, BeautifulPictures> hash = redisTemplate.opsForHash();</div><div class="line"> hash.put(<span class="string">"test5"</span>,beautifulPictures.getId(),beautifulPictures);</div><div class="line"> System.out.println(hash.get(<span class="string">"test5"</span>, beautifulPictures.getId()));</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景六:使用lists有序存储读取"><a href="#场景六:使用lists有序存储读取" class="headerlink" title="场景六:使用lists有序存储读取"></a>场景六:使用lists有序存储读取</h1><blockquote>
<p>适用于获取最近N个操作的数据。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//使用lists存储读取 有序</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test6</span><span class="params">()</span></span>{</div><div class="line"> ListOperations<String, String> list = stringRedisTemplate.opsForList();</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"1"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"2"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"3"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"4"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"5"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"6"</span>);</div><div class="line"> list.leftPush(<span class="string">"test6"</span>, <span class="string">"7"</span>);</div><div class="line"> <span class="comment">//保持链表只有3位</span></div><div class="line"> list.trim(<span class="string">"test6"</span>, <span class="number">0</span>, <span class="number">2</span>);</div><div class="line"> System.out.println(list.range(<span class="string">"test6"</span>, <span class="number">0</span>, list.size(<span class="string">"test6"</span>)-<span class="number">1</span>));</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景七:使用sets存储读取-无序-去重-求差集,交集,并集"><a href="#场景七:使用sets存储读取-无序-去重-求差集,交集,并集" class="headerlink" title="场景七:使用sets存储读取 无序 去重 求差集,交集,并集"></a>场景七:使用sets存储读取 无序 去重 求差集,交集,并集</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//使用set存储读取 无序 去重 求差集,交集,并集</span></div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test7</span><span class="params">()</span></span>{</div><div class="line"> SetOperations<String, String> set = stringRedisTemplate.opsForSet();</div><div class="line"> set.add(<span class="string">"test7_1"</span>, <span class="string">"2"</span>, <span class="string">"1"</span>,<span class="string">"2"</span>,<span class="string">"3"</span>,<span class="string">"4"</span>,<span class="string">"4"</span>,<span class="string">"3"</span>);</div><div class="line"> set.add(<span class="string">"test7_2"</span>, <span class="string">"2"</span>, <span class="string">"6"</span>,<span class="string">"2"</span>,<span class="string">"3"</span>,<span class="string">"7"</span>,<span class="string">"6"</span>,<span class="string">"5"</span>);</div><div class="line"> System.out.println(<span class="string">"全部成员"</span>+set.members(<span class="string">"test7_1"</span>));</div><div class="line"> System.out.println(<span class="string">"差集"</span>+set.difference(<span class="string">"test7_1"</span>, <span class="string">"test7_2"</span>));</div><div class="line"> System.out.println(<span class="string">"交集"</span>+set.intersect(<span class="string">"test7_1"</span>, <span class="string">"test7_2"</span>));</div><div class="line"> System.out.println(<span class="string">"并集"</span>+set.union(<span class="string">"test7_1"</span>, <span class="string">"test7_2"</span>));</div><div class="line"> }</div></pre></td></tr></table></figure>
<h1 id="场景八:Sorted-Set-存取数据-排序"><a href="#场景八:Sorted-Set-存取数据-排序" class="headerlink" title="场景八:Sorted Set 存取数据 排序"></a>场景八:Sorted Set 存取数据 排序</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">//Sorted Set 存取数据 排序 相比sets 保存时多一个权重参数score,相当于按照此参数来排序</div><div class="line"> @Test</div><div class="line"> public void test8(){</div><div class="line"> ZSetOperations<String, String> zSet = stringRedisTemplate.opsForZSet();</div><div class="line"> zSet.add("test8", "use1", 9);</div><div class="line"> zSet.add("test8", "use2", 1);</div><div class="line"> zSet.add("test8", "use3", 5);</div><div class="line"> zSet.add("test8", "use4", 9);</div><div class="line"> //对应的score值增加</div><div class="line"> //zSet.incrementScore("test8", "use1", 1);</div><div class="line"> System.out.println(zSet.reverseRange("test8", 0, zSet.size("test8")-1));</div><div class="line"> }</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>之前利用springBoot中的缓存机制,使用Redis作为缓存容器,做了一个缓存的简单Demo,当然Redis不仅仅可以用来做缓存的容器,还有很多开发的实际场景中会用到Redis的特性,通过几天的学习,现将学习的成果整理分享给大家。希望大家也多多
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="redis" scheme="http://z77z.oschina.io/tags/redis/"/>
</entry>
<entry>
<title>SpringBoot缓存注解学习笔记之Redis</title>
<link href="http://z77z.oschina.io/2017/01/30/SpringBoot%E7%BC%93%E5%AD%98%E6%B3%A8%E8%A7%A3%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8BRedis/"/>
<id>http://z77z.oschina.io/2017/01/30/SpringBoot缓存注解学习笔记之Redis/</id>
<published>2017-01-30T08:41:58.000Z</published>
<updated>2017-01-30T08:41:26.298Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>之前用SpringBoot+MyBatisPlus+SpringMVC整合搭建了一个基础web开发框架,使用这三个框架搭建出来项目结构非常的清爽,没有过多的配置文件,各个模块之间有清晰的联系,非常适合敏捷开发。</p>
<p>最近学习了Redis这个基于内存的,Key-Value数据形式的高性能数据库,感觉学习了入门之后很简单,没有体会到它具体能干嘛,我就想着使用Redis这个数据库来整合之前搭建的框架,利用Spring中的缓存机制,将查询的信息缓存到Redis中。</p>
</blockquote>
<h2 id="安装Redis"><a href="#安装Redis" class="headerlink" title="安装Redis"></a>安装Redis</h2><blockquote>
<p>Window 下安装 下载地址:<a href="https://github.com/MSOpenTech/redis/relea" target="_blank" rel="external">https://github.com/MSOpenTech/redis/relea</a>ses。 Redis 支持32 位和64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C盘的tools目录中,解压后,将文件夹重新命名为 redis。</p>
</blockquote>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130104722921?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="下载Redis版本" title="">
</div>
<div class="image-caption">下载Redis版本</div>
</figure>
<blockquote>
<p>打开一个 cmd 窗口 使用cd命令切换目录到 C:\tools\redis 运行 redis-server.exe redis.windows.conf 。<br>如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:</p>
</blockquote>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130104843343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="启动成功后" title="">
</div>
<div class="image-caption">启动成功后</div>
</figure>
<blockquote>
<p>启动成功后不要关闭命令窗口,不然redis服务也会关闭。如果不想每次使用都一直开着这个命令窗口,可以将redis服务添加到windows的服务中:</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">安装命令:</div><div class="line">redis-server.exe --service-install redis.windows.conf --loglevel verbose </div><div class="line"></div><div class="line">卸载命令:</div><div class="line">redis-server --service-uninstall</div></pre></td></tr></table></figure>
<blockquote>
<p> 安装成功后,可以在windows的服务管理中对redis进行管理,就不用每次都打开命令窗口来启动redis服务了,如下图:</p>
</blockquote>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130105834360?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="在windows中管理redis服务" title="">
</div>
<div class="image-caption">在windows中管理redis服务</div>
</figure>
<h2 id="获取之前项目"><a href="#获取之前项目" class="headerlink" title="获取之前项目"></a>获取之前项目</h2><blockquote>
<p>环境我就直接在之前的整合框架上进行搭建,之前项目下载地址:<a href="https://git.oschina.net/z77z/springboot_mybatisplus" target="_blank" rel="external">https://git.oschina.net/z77z/springboot_mybatisplus</a><br>注意:之前搭建这个框架的时候我为了获取基础数据,在启动springboot的时候也启动了爬虫程序,如果不想每次启动都启动爬虫可以注释掉启动类中的run方法。</p>
</blockquote>
<h2 id="添加Redis依赖到pom-xml中"><a href="#添加Redis依赖到pom-xml中" class="headerlink" title="添加Redis依赖到pom.xml中"></a>添加Redis依赖到pom.xml中</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-redis<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"><span class="tag"></<span class="name">dependency</span>></span></div></pre></td></tr></table></figure>
<h2 id="在application-properties中添加配置"><a href="#在application-properties中添加配置" class="headerlink" title="在application.properties中添加配置"></a>在application.properties中添加配置</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"># REDIS (RedisProperties)</div><div class="line"># Redis数据库索引(默认为0)</div><div class="line">spring.redis.database=0</div><div class="line"># Redis服务器地址</div><div class="line">spring.redis.host=localhost</div><div class="line"># Redis服务器连接端口</div><div class="line">spring.redis.port=6379</div><div class="line"># Redis服务器连接密码(默认为空)</div><div class="line">spring.redis.password=</div><div class="line"># 连接池最大连接数(使用负值表示没有限制)</div><div class="line">spring.redis.pool.max-active=8</div><div class="line"># 连接池最大阻塞等待时间(使用负值表示没有限制)</div><div class="line">spring.redis.pool.max-wait=-1</div><div class="line"># 连接池中的最大空闲连接</div><div class="line">spring.redis.pool.max-idle=8</div><div class="line"># 连接池中的最小空闲连接</div><div class="line">spring.redis.pool.min-idle=0</div><div class="line"># 连接超时时间(毫秒)</div><div class="line">spring.redis.timeout=0</div></pre></td></tr></table></figure>
<blockquote>
<p>我们需要做的配置到这里就已经完成了,Spring Boot会在侦测到存在Redis的依赖并且Redis的配置是可用的情况下,使用RedisCacheManager初始化CacheManager。也就是说要使用缓存的话,SpringBoot就会选择Redis来作为缓存的容器。</p>
</blockquote>
<h2 id="编写一个简单的redis读写测试列"><a href="#编写一个简单的redis读写测试列" class="headerlink" title="编写一个简单的redis读写测试列"></a>编写一个简单的redis读写测试列</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * redis读写测试</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="meta">@RunWith</span>(SpringJUnit4ClassRunner.class)</div><div class="line"><span class="meta">@SpringBootTest</span>(classes = Application.class)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCacheTest</span> </span>{</div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> StringRedisTemplate stringRedisTemplate;</div><div class="line"> </div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">redisTest</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"></div><div class="line"> <span class="comment">//保存字符串</span></div><div class="line"> stringRedisTemplate.opsForValue().set(<span class="string">"aaa"</span>, <span class="string">"111"</span>);</div><div class="line"> <span class="comment">//读取字符串</span></div><div class="line"> String aaa = stringRedisTemplate.opsForValue().get(<span class="string">"aaa"</span>);</div><div class="line"> System.out.println(aaa);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>打印结果:</strong></p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130150029914?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="redis读写测试结果" title="">
</div>
<div class="image-caption">redis读写测试结果</div>
</figure>
<h2 id="编写缓存测试列"><a href="#编写缓存测试列" class="headerlink" title="编写缓存测试列"></a>编写缓存测试列</h2><p><strong>不使用缓存:</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 获取数据,并且做缓存处理</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCache</span> </span>{</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"> <span class="comment">//@Cacheable(value = "BeautifulPictures")</span></div><div class="line"> <span class="function"><span class="keyword">public</span> BeautifulPictures <span class="title">getBeautifulPicturesList</span><span class="params">(String id)</span> </span>{</div><div class="line"> <span class="keyword">return</span> beautifulPicturesService.selectById(id);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * 测试类</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="meta">@RunWith</span>(SpringJUnit4ClassRunner.class)</div><div class="line"><span class="meta">@SpringBootTest</span>(classes = Application.class)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCacheTest</span> </span>{</div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"></div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> StringRedisTemplate stringRedisTemplate;</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> RedisCache redisCache;</div><div class="line"> </div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">redisTest</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"></div><div class="line"> <span class="comment">//保存字符串</span></div><div class="line"> stringRedisTemplate.opsForValue().set(<span class="string">"aaa"</span>, <span class="string">"111"</span>);</div><div class="line"> <span class="comment">//读取字符串</span></div><div class="line"> String aaa = stringRedisTemplate.opsForValue().get(<span class="string">"aaa"</span>);</div><div class="line"> System.out.println(aaa);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">CacheTest</span><span class="params">()</span> </span>{</div><div class="line"> BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList(<span class="string">"1011"</span>);</div><div class="line"> System.out.println(<span class="string">"第一次查询结果:"</span>);</div><div class="line"> System.out.println(beautifulPicture);</div><div class="line"></div><div class="line"> BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList(<span class="string">"1011"</span>);</div><div class="line"> System.out.println(<span class="string">"第二次查询结果:"</span>);</div><div class="line"> System.out.println(beautifulPicture1);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>执行结果:</p>
<p><img src="http://img.blog.csdn.net/20170130151152443?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="不使用缓存测试结果"></p>
<blockquote>
<p>可以从日志中看出两次查询都是执行了sql,也就是执行了<code>getBeautifulPicturesList</code>这个方法</p>
</blockquote>
<p><strong>使用缓存:</strong></p>
<blockquote>
<p>在getBeautifulPicturesList方法的上面添加@Cacheable(value = “BeautifulPictures”)注解,当请求这个方法时会先判断缓存中是否存在,存在就在缓存中获取,不会执行这个方法。不存在就正常执行这个方法获取返回值并且存如缓存中。添加注解后执行结果如下:</p>
</blockquote>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130152127113?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="添加缓存注释后" title="">
</div>
<div class="image-caption">添加缓存注释后</div>
</figure>
<blockquote>
<p>从日志中可以看出,第一次查询的时候执行的sql,而第二次查询的时候没有执行sql,说明是从缓存中获取的数据。</p>
</blockquote>
<h2 id="缓存数据一致性保证"><a href="#缓存数据一致性保证" class="headerlink" title="缓存数据一致性保证"></a>缓存数据一致性保证</h2><blockquote>
<p>CRUD (Create 创建,Retrieve 读取,Update 更新,Delete 删除) 操作中,除了 R具备幂等性,其他三个发生的时候都可能会造成缓存结果和数据库不一致。为了保证缓存数据的一致性,在进行 CUD 操作的时候我们需要对可能影响到的缓存进行更新或者清除。如下:</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 获取数据,并且做缓存处理</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCache</span> </span>{</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"> </div><div class="line"> <span class="comment">//查询</span></div><div class="line"> <span class="meta">@Cacheable</span>(value = <span class="string">"beautifulPictures"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> BeautifulPictures <span class="title">getBeautifulPicturesList</span><span class="params">(String id)</span> </span>{</div><div class="line"> <span class="keyword">return</span> beautifulPicturesService.selectById(id);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="comment">//修改</span></div><div class="line"> <span class="meta">@CachePut</span>(value = <span class="string">"beautifulPictures"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">updateBeautifulPicture</span><span class="params">(String id)</span> </span>{</div><div class="line"> BeautifulPictures beautifulPictures = <span class="keyword">new</span> BeautifulPictures();</div><div class="line"> beautifulPictures.setTitle(<span class="string">"Title被我修改了一下,哈哈"</span>);</div><div class="line"> beautifulPictures.setId(id);</div><div class="line"> beautifulPicturesService.updateById(beautifulPictures);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * 测试类</div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="meta">@RunWith</span>(SpringJUnit4ClassRunner.class)</div><div class="line"><span class="meta">@SpringBootTest</span>(classes = Application.class)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisCacheTest</span> </span>{</div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"></div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> StringRedisTemplate stringRedisTemplate;</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> RedisCache redisCache;</div><div class="line"> </div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">redisTest</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"></div><div class="line"> <span class="comment">//保存字符串</span></div><div class="line"> stringRedisTemplate.opsForValue().set(<span class="string">"aaa"</span>, <span class="string">"111"</span>);</div><div class="line"> <span class="comment">//读取字符串</span></div><div class="line"> String aaa = stringRedisTemplate.opsForValue().get(<span class="string">"aaa"</span>);</div><div class="line"> System.out.println(aaa);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="meta">@Test</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">CacheTest</span><span class="params">()</span> </span>{</div><div class="line"> String id = <span class="string">"1"</span>;</div><div class="line"> BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList(id);</div><div class="line"> System.out.println(<span class="string">"第一次查询结果:"</span>);</div><div class="line"> System.out.println(beautifulPicture);</div><div class="line"></div><div class="line"> BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList(id);</div><div class="line"> System.out.println(<span class="string">"第二次查询结果:"</span>);</div><div class="line"> System.out.println(beautifulPicture1);</div><div class="line"> </div><div class="line"> redisCache.updateBeautifulPicture(id);</div><div class="line"> </div><div class="line"> BeautifulPictures beautifulPicture2 = redisCache.getBeautifulPicturesList(id);</div><div class="line"> System.out.println(<span class="string">"第三次查询结果:"</span>);</div><div class="line"> System.out.println(beautifulPicture2);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>保持缓存一致性测试结果:</strong></p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170130160141130?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="保持缓存的一致性测试" title="">
</div>
<div class="image-caption">保持缓存的一致性测试</div>
</figure>
<blockquote>
<p>在会导致数据发生改变的方法上添加<code>@CachePut(value = "beautifulPictures")</code>注解,添加后会更新缓存中的值,并且每次都会正常执行方法内容。</p>
</blockquote>
<h2 id="SpringBoot缓存注解详解"><a href="#SpringBoot缓存注解详解" class="headerlink" title="SpringBoot缓存注解详解"></a>SpringBoot缓存注解详解</h2><ul>
<li>@Cacheable:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存</li>
</ul>
<blockquote>
<p>主要参数说明:</p>
<ol>
<li><code>value</code>:缓存的名称,在 spring 配置文件中定义,必须指定至少一个,例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}。</li>
<li><code>key</code>:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,例如:@Cacheable(value=”testcache”,key=”#userName”)。</li>
<li><code>condition</code>:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存,例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2。</li>
</ol>
</blockquote>
<ul>
<li>@CachePut:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用</li>
</ul>
<blockquote>
<p>主要参数说明:</p>
<ol>
<li><code>value</code>,<code>key</code>和<code>condition</code>参数配置和@Cacheable一样。</li>
</ol>
</blockquote>
<ul>
<li>@CacheEvict:作用是主要针对方法配置,能够根据一定的条件对缓存进行清空</li>
</ul>
<blockquote>
<p>主要参数说明:</p>
<ol>
<li><code>value</code>,<code>key</code>和<code>condition</code>参数配置和@Cacheable一样。</li>
<li><code>allEntries</code>:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存,例如:@CachEvict(value=”testcache”,allEntries=true)。</li>
<li><code>beforeInvocation</code>:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存,例如@CachEvict(value=”testcache”,beforeInvocation=true)。</li>
</ol>
</blockquote>
<p><strong>另外说下: @cache(“something”);这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。</strong></p>
<h2 id="荆轲-刺秦王"><a href="#荆轲-刺秦王" class="headerlink" title="荆轲~刺秦王~~"></a>荆轲~刺秦王~~</h2>]]></content>
<summary type="html">
<blockquote>
<p>之前用SpringBoot+MyBatisPlus+SpringMVC整合搭建了一个基础web开发框架,使用这三个框架搭建出来项目结构非常的清爽,没有过多的配置文件,各个模块之间有清晰的联系,非常适合敏捷开发。</p>
<p>最近学习了Redis这
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="redis" scheme="http://z77z.oschina.io/tags/redis/"/>
<category term="缓存" scheme="http://z77z.oschina.io/tags/%E7%BC%93%E5%AD%98/"/>
</entry>
<entry>
<title>SpringBoot+SpringMVC+MybatisPlus框架整合练习之【美女图片】爬虫---图文详细流程</title>
<link href="http://z77z.oschina.io/2017/01/23/SpringBoot+SpringMVC+MybatisPlus%E6%A1%86%E6%9E%B6%E6%95%B4%E5%90%88%E7%BB%83%E4%B9%A0%E4%B9%8B%E3%80%90%E7%BE%8E%E5%A5%B3%E5%9B%BE%E7%89%87%E3%80%91%E7%88%AC%E8%99%AB---%E5%9B%BE%E6%96%87%E8%AF%A6%E7%BB%86%E6%B5%81%E7%A8%8B/"/>
<id>http://z77z.oschina.io/2017/01/23/SpringBoot+SpringMVC+MybatisPlus框架整合练习之【美女图片】爬虫---图文详细流程/</id>
<published>2017-01-22T16:25:58.000Z</published>
<updated>2017-01-30T09:20:54.550Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>最近浏览很多博客,学习了不少新的知识,收获颇多,就想着能不能将新学的知识整合一下来练练手,提高自己撸代码搭框架的能力,还有就是给大家一个新年福利,爬一爬美女图片网站。上车请刷卡,哈哈。顺便就拿这爬来的数据作为基础数据来整合最近学习的框架。一劳多得。当然,本文还是以框架整合为主,爬取美女图片只是为了获取基础数据而已啦!!</p>
<p>现在我将我的学习成果分享出来,还望各位大神多多指点,有些不规范的地方还望大家指出,多多讨论学习,共同进步。源码我已经托管到我的码云上面,大家可以进我的博客查看,如果想要图片资源的话在评论区留下邮箱吧。</p>
</blockquote>
<p>、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、<br><strong>个人博客地址:<a href="http://z77z.oschina.io/">http://z77z.oschina.io/</a></strong><br>,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,</p>
<h1 id="先上成果(19禁!!)"><a href="#先上成果(19禁!!)" class="headerlink" title="先上成果(19禁!!)"></a>先上成果(19禁!!)</h1><p>爬取的图片,一共爬了<strong>一万多张</strong>,够大家玩一阵了。。。——–羞涩。。</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170122210346811?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="爬取的图片" title="">
</div>
<div class="image-caption">爬取的图片</div>
</figure>
<p>数据库:</p>
<p>存储每个图片集合的链接,标题等信息。</p>
<p> <img src="http://img.blog.csdn.net/20170122210921243?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="文章信息表"></p>
<p>存储每张图片的链接,并且和图片集合表关联。</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170122211209525?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="图片信息表" title="">
</div>
<div class="image-caption">图片信息表</div>
</figure>
<p>项目整体搭建出来后的效果</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170122211603093?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="项目文件结构" title="">
</div>
<div class="image-caption">项目文件结构</div>
</figure>
<h1 id="使用工具,框架介绍"><a href="#使用工具,框架介绍" class="headerlink" title="使用工具,框架介绍"></a>使用工具,框架介绍</h1><blockquote>
<p>这里只介绍下最近学习的框架,其他在后面用到在做介绍。这两个框架都是对现有的spring和mybatis的一个提升,而并不是替代之前的框架,使开发者能够达到敏捷开发的目的。只推荐老司机学习,新手还是建议从基础入手。</p>
</blockquote>
<ul>
<li>SpringBoot</li>
</ul>
<blockquote>
<p>Spring由于其繁琐的配置,一度被人认为“配置地狱”,各种XML、Annotation配置,让人眼花缭乱,而且如果出错了也很难找出原因。想想之前搭建一个SSM框架,配置文件相当恼火,springboot的出现就大大减少了这些配置,甚至可以零配置文件。这里推荐一个此框架学习的博客链接:<a href="http://blog.720ui.com/page/3/" target="_blank" rel="external">http://blog.720ui.com/page/3/</a></p>
</blockquote>
<ul>
<li>MybatisPlus:</li>
</ul>
<blockquote>
<p> 这个框架是国内的大神编写的,我个人认为这就是一个mybatis的一个增强工具包,好处请大家自行去官方文档查阅,这里就不再赘述了。文档链接:<a href="http://mp.baomidou.com/docs/index.html" target="_blank" rel="external">http://mp.baomidou.com/docs/index.html</a></p>
</blockquote>
<h1 id="建立数据库"><a href="#建立数据库" class="headerlink" title="建立数据库"></a>建立数据库</h1><blockquote>
<p>这里数据来源是爬虫爬取的。这里爬虫本身就不多介绍,我之前已经写过爬虫相关文章,出门左拐,我的个人博客中。</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line">/*</div><div class="line">Navicat MySQL Data Transfer</div><div class="line"></div><div class="line">Source Server : 本地</div><div class="line">Source Server Version : 50610</div><div class="line">Source Host : localhost:3306</div><div class="line">Source Database : crawler</div><div class="line"></div><div class="line">Target Server Type : MYSQL</div><div class="line">Target Server Version : 50610</div><div class="line">File Encoding : 65001</div><div class="line"></div><div class="line">Date: 2017-01-22 22:40:13</div><div class="line">*/</div><div class="line"></div><div class="line">SET FOREIGN_KEY_CHECKS=0;</div><div class="line"></div><div class="line">-- ----------------------------</div><div class="line">-- Table structure for beautiful_pictures</div><div class="line">-- ----------------------------</div><div class="line">DROP TABLE IF EXISTS `beautiful_pictures`;</div><div class="line">CREATE TABLE `beautiful_pictures` (</div><div class="line"> `id` varchar(255) NOT NULL COMMENT '美女图片爬取',</div><div class="line"> `title` varchar(255) DEFAULT NULL,</div><div class="line"> `url` varchar(255) DEFAULT NULL,</div><div class="line"> `pictureurls_num` int(11) DEFAULT NULL,</div><div class="line"> `zan` int(11) DEFAULT NULL,</div><div class="line"> `biaoqian` varchar(255) DEFAULT NULL,</div><div class="line"> `keywords` varchar(255) DEFAULT NULL,</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8;</div><div class="line"></div><div class="line">-- ----------------------------</div><div class="line">-- Table structure for picture</div><div class="line">-- ----------------------------</div><div class="line">DROP TABLE IF EXISTS `picture`;</div><div class="line">CREATE TABLE `picture` (</div><div class="line"> `id` varchar(255) NOT NULL COMMENT '每张图片的地址',</div><div class="line"> `pictures_id` varchar(255) DEFAULT NULL,</div><div class="line"> `url` varchar(255) DEFAULT NULL,</div><div class="line"> PRIMARY KEY (`id`)</div><div class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8;</div></pre></td></tr></table></figure>
<h1 id="整合框架的搭建"><a href="#整合框架的搭建" class="headerlink" title="整合框架的搭建"></a>整合框架的搭建</h1><h2 id="新建项目添加配置文件"><a href="#新建项目添加配置文件" class="headerlink" title="新建项目添加配置文件"></a>新建项目添加配置文件</h2><blockquote>
<p>我这里使用的IDE工具是Spring Tool Suite,spring开发的首选开发工具<br> 新建一个maven project ,选择maven-archetypr-webapp这个文件结构模版。<br> 配置pom.xml文件和application.properties文件</p>
</blockquote>
<p>pom.xml</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">"http://maven.apache.org/POM/4.0.0"</span> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></div><div class="line"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></div><div class="line"> <span class="tag"><<span class="name">modelVersion</span>></span>4.0.0<span class="tag"></<span class="name">modelVersion</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.baomidou<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mybatisplus-spring-boot<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">packaging</span>></span>war<span class="tag"></<span class="name">packaging</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.0<span class="tag"></<span class="name">version</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">parent</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-parent<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.4.1.RELEASE<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">parent</span>></span></div><div class="line"> </div><div class="line"> <span class="tag"><<span class="name">dependencies</span>></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-web<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"> </div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.tomcat.embed<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>tomcat-embed-jasper<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.mybatis.spring.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mybatis-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.1.1<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>Mysql<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mysql-connector-java<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>5.1.38<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- mybatisPlus代码生成模板引擎 --></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.velocity<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>velocity<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- druid阿里巴巴数据库连接池 --></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.alibaba<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>druid<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.0.26<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"> </div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.zaxxer<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>HikariCP<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- MP 核心库 --></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.baomidou<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mybatis-plus<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.0.1<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- JUnit test dependency --></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.jayway.restassured<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>rest-assured<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>2.3.3<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"> <span class="comment"><!-- fastjson阿里巴巴jSON处理器 --></span></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.alibaba<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>fastjson<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.2.13<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.jsoup<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jsoup<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"><<span class="name">version</span>></span>1.10.2<span class="tag"></<span class="name">version</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependency</span>></span></div><div class="line"> <span class="tag"></<span class="name">dependencies</span>></span></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">build</span>></span></div><div class="line"> <span class="tag"><<span class="name">plugins</span>></span></div><div class="line"> <span class="tag"><<span class="name">plugin</span>></span></div><div class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></div><div class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-maven-plugin<span class="tag"></<span class="name">artifactId</span>></span></div><div class="line"> <span class="tag"></<span class="name">plugin</span>></span></div><div class="line"> <span class="tag"></<span class="name">plugins</span>></span></div><div class="line"> <span class="tag"></<span class="name">build</span>></span></div><div class="line"><span class="tag"></<span class="name">project</span>></span></div></pre></td></tr></table></figure>
<p>application.properties</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line">#view</div><div class="line">spring.mvc.view.prefix=/WEB-INF/view/</div><div class="line">spring.mvc.view.suffix=.jsp</div><div class="line"></div><div class="line"># jdbc_config</div><div class="line">spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crawler?characterEncoding=utf8</div><div class="line">spring.datasource.username=root</div><div class="line">spring.datasource.password=</div><div class="line">spring.datasource.driver-class-name=com.mysql.jdbc.Driver</div><div class="line">spring.datasource.type=com.alibaba.druid.pool.DruidDataSource</div><div class="line"></div><div class="line">#druid_config</div><div class="line">spring.datasource.max-active: 20</div><div class="line">spring.datasource.initial-size: 1</div><div class="line">spring.datasource.min-idle: 3</div><div class="line">spring.datasource.max-wait: 60000</div><div class="line">spring.datasource.time-between-eviction-runs-millis: 60000</div><div class="line">spring.datasource.min-evictable-idle-time-millis: 300000</div><div class="line">spring.datasource.test-while-idle: true</div><div class="line">spring.datasource.test-on-borrow: false</div><div class="line">spring.datasource.test-on-return: false</div><div class="line">spring.datasource.poolPreparedStatements: true</div><div class="line"></div><div class="line"></div><div class="line"></div><div class="line"># mybatis_config</div><div class="line">mybatis.mapper-locations=classpath:io/z77z/mapper/*Mapper.xml </div><div class="line">mybatis.typeAliasesPackage=io.z77z.entity</div><div class="line"></div><div class="line"># log_config DEBUG ERROR INFO WARN </div><div class="line">logging.level.root=WARN</div><div class="line">logging.file=./logs/spring-boot-logging.log</div></pre></td></tr></table></figure>
<blockquote>
<p>如果pom.xml报错,大部分原因都是因为jar包没有下载成功,可以手动下载后放到maven本地仓库里面,阿里maven镜像仓库链接:<a href="http://maven.aliyun.com/nexus/#welcome" target="_blank" rel="external">http://maven.aliyun.com/nexus/#welcome</a></p>
</blockquote>
<h2 id="编写MybatisPlusConfig-java"><a href="#编写MybatisPlusConfig-java" class="headerlink" title="编写MybatisPlusConfig.java"></a>编写MybatisPlusConfig.java</h2><blockquote>
<p>对mybatisplus的一些配置,配置成Bean交给spring容器管理</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Bean</span></div><div class="line"><span class="function"><span class="keyword">public</span> PaginationInterceptor <span class="title">paginationInterceptor</span><span class="params">()</span> </span>{</div><div class="line"> PaginationInterceptor page = <span class="keyword">new</span> PaginationInterceptor();</div><div class="line"> page.setDialectType(<span class="string">"mysql"</span>);</div><div class="line"> <span class="keyword">return</span> page;</div><div class="line">}</div><div class="line"><span class="comment">/**</span></div><div class="line"> * 这里全部使用mybatis-autoconfigure 已经自动加载的资源。不手动指定</div><div class="line"> * 配置文件和mybatis-boot的配置文件同步</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"><span class="meta">@Bean</span></div><div class="line"><span class="function"><span class="keyword">public</span> MybatisSqlSessionFactoryBean <span class="title">mybatisSqlSessionFactoryBean</span><span class="params">()</span> </span>{</div><div class="line"> MybatisSqlSessionFactoryBean mybatisPlus = <span class="keyword">new</span> MybatisSqlSessionFactoryBean();</div><div class="line"> mybatisPlus.setDataSource(dataSource);</div><div class="line"> mybatisPlus.setVfs(SpringBootVFS.class);</div><div class="line"> <span class="keyword">if</span> (StringUtils.hasText(<span class="keyword">this</span>.properties.getConfigLocation())) {</div><div class="line"> mybatisPlus.setConfigLocation(<span class="keyword">this</span>.resourceLoader.getResource(<span class="keyword">this</span>.properties.getConfigLocation()));</div><div class="line"> }</div><div class="line"> mybatisPlus.setConfiguration(properties.getConfiguration());</div><div class="line"> <span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="keyword">this</span>.interceptors)) {</div><div class="line"> mybatisPlus.setPlugins(<span class="keyword">this</span>.interceptors);</div><div class="line"> }</div><div class="line"> <span class="comment">// MP 全局配置,更多内容进入类看注释</span></div><div class="line"> GlobalConfiguration globalConfig = <span class="keyword">new</span> GlobalConfiguration();</div><div class="line"> globalConfig.setDbType(DBType.MYSQL.name());</div><div class="line"> <span class="comment">// ID 策略 AUTO->`0`("数据库ID自增") INPUT->`1`(用户输入ID") ID_WORKER->`2`("全局唯一ID") UUID->`3`("全局唯一ID")</span></div><div class="line"> globalConfig.setIdType(<span class="number">3</span>);</div><div class="line"> mybatisPlus.setGlobalConfig(globalConfig);</div><div class="line"> MybatisConfiguration mc = <span class="keyword">new</span> MybatisConfiguration();</div><div class="line"> mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);</div><div class="line"> mybatisPlus.setConfiguration(mc);</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.databaseIdProvider != <span class="keyword">null</span>) {</div><div class="line"> mybatisPlus.setDatabaseIdProvider(<span class="keyword">this</span>.databaseIdProvider);</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (StringUtils.hasLength(<span class="keyword">this</span>.properties.getTypeAliasesPackage())) {</div><div class="line"> mybatisPlus.setTypeAliasesPackage(<span class="keyword">this</span>.properties.getTypeAliasesPackage());</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (StringUtils.hasLength(<span class="keyword">this</span>.properties.getTypeHandlersPackage())) {</div><div class="line"> mybatisPlus.setTypeHandlersPackage(<span class="keyword">this</span>.properties.getTypeHandlersPackage());</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="keyword">this</span>.properties.resolveMapperLocations())) {</div><div class="line"> mybatisPlus.setMapperLocations(<span class="keyword">this</span>.properties.resolveMapperLocations());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> mybatisPlus;</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="使用mybatisplus的代码生成插件"><a href="#使用mybatisplus的代码生成插件" class="headerlink" title="使用mybatisplus的代码生成插件"></a>使用mybatisplus的代码生成插件</h2><p>MpGenerator.java</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 代码生成器演示</div><div class="line"> * </p></div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MpGenerator</span> </span>{</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * MySQL 生成演示</div><div class="line"> * </p></div><div class="line"> */</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> AutoGenerator mpg = <span class="keyword">new</span> AutoGenerator();</div><div class="line"> <span class="comment">// 全局配置</span></div><div class="line"> GlobalConfig gc = <span class="keyword">new</span> GlobalConfig();</div><div class="line"> gc.setOutputDir(<span class="string">"C://"</span>);</div><div class="line"> gc.setFileOverride(<span class="keyword">true</span>);</div><div class="line"> gc.setActiveRecord(<span class="keyword">true</span>);</div><div class="line"> gc.setEnableCache(<span class="keyword">false</span>);<span class="comment">// XML 二级缓存</span></div><div class="line"> gc.setBaseResultMap(<span class="keyword">true</span>);<span class="comment">// XML ResultMap</span></div><div class="line"> gc.setBaseColumnList(<span class="keyword">false</span>);<span class="comment">// XML columList</span></div><div class="line"> gc.setAuthor(<span class="string">"z77z"</span>);</div><div class="line"> <span class="comment">// 自定义文件命名,注意 %s 会自动填充表实体属性!</span></div><div class="line"> <span class="comment">// gc.setMapperName("%sDao");</span></div><div class="line"> <span class="comment">// gc.setXmlName("%sDao");</span></div><div class="line"> <span class="comment">// gc.setServiceName("MP%sService");</span></div><div class="line"> <span class="comment">// gc.setServiceImplName("%sServiceDiy");</span></div><div class="line"> <span class="comment">// gc.setControllerName("%sAction");</span></div><div class="line"> mpg.setGlobalConfig(gc);</div><div class="line"> <span class="comment">// 数据源配置</span></div><div class="line"> DataSourceConfig dsc = <span class="keyword">new</span> DataSourceConfig();</div><div class="line"> dsc.setDbType(DbType.MYSQL);</div><div class="line"> dsc.setDriverName(<span class="string">"com.mysql.jdbc.Driver"</span>);</div><div class="line"> dsc.setUsername(<span class="string">"root"</span>);</div><div class="line"> dsc.setPassword(<span class="string">""</span>);</div><div class="line"> dsc.setUrl(<span class="string">"jdbc:mysql://127.0.0.1:3306/crawler?characterEncoding=utf8"</span>);</div><div class="line"> mpg.setDataSource(dsc);</div><div class="line"> <span class="comment">// 策略配置</span></div><div class="line"> StrategyConfig strategy = <span class="keyword">new</span> StrategyConfig();</div><div class="line"> <span class="comment">//strategy.setTablePrefix("beautiful_");// 此处可以修改为您的表前缀</span></div><div class="line"> strategy.setNaming(NamingStrategy.underline_to_camel);<span class="comment">// 表名生成策略</span></div><div class="line"> strategy.setInclude(<span class="keyword">new</span> String[] { <span class="string">"beautiful_pictures"</span> }); <span class="comment">// 需要生成的表</span></div><div class="line"> strategy.setInclude(<span class="keyword">new</span> String[] { <span class="string">"picture"</span> }); <span class="comment">// 需要生成的表</span></div><div class="line"> <span class="comment">// strategy.setExclude(new String[]{"test"}); // 排除生成的表</span></div><div class="line"> <span class="comment">// 字段名生成策略</span></div><div class="line"> strategy.setFieldNaming(NamingStrategy.underline_to_camel);</div><div class="line"> <span class="comment">// 自定义实体父类</span></div><div class="line"> <span class="comment">// strategy.setSuperEntityClass("com.baomidou.demo.TestEntity");</span></div><div class="line"> <span class="comment">// 自定义实体,公共字段</span></div><div class="line"> <span class="comment">// strategy.setSuperEntityColumns(new String[] { "test_id", "age" });</span></div><div class="line"> <span class="comment">// 自定义 mapper 父类</span></div><div class="line"> <span class="comment">// strategy.setSuperMapperClass("com.baomidou.demo.TestMapper");</span></div><div class="line"> <span class="comment">// 自定义 service 父类</span></div><div class="line"> <span class="comment">// strategy.setSuperServiceClass("com.baomidou.demo.TestService");</span></div><div class="line"> <span class="comment">// 自定义 service 实现类父类</span></div><div class="line"> <span class="comment">// strategy.setSuperServiceImplClass("com.baomidou.demo.TestServiceImpl");</span></div><div class="line"> <span class="comment">// 自定义 controller 父类</span></div><div class="line"> <span class="comment">// strategy.setSuperControllerClass("com.baomidou.demo.TestController");</span></div><div class="line"> <span class="comment">// 【实体】是否生成字段常量(默认 false)</span></div><div class="line"> <span class="comment">// public static final String ID = "test_id";</span></div><div class="line"> <span class="comment">// strategy.setEntityColumnConstant(true);</span></div><div class="line"> <span class="comment">// 【实体】是否为构建者模型(默认 false)</span></div><div class="line"> <span class="comment">// public User setName(String name) {this.name = name; return this;}</span></div><div class="line"> <span class="comment">// strategy.setEntityBuliderModel(true);</span></div><div class="line"> mpg.setStrategy(strategy);</div><div class="line"> <span class="comment">// 包配置</span></div><div class="line"> PackageConfig pc = <span class="keyword">new</span> PackageConfig();</div><div class="line"> pc.setParent(<span class="string">"io"</span>);</div><div class="line"> pc.setModuleName(<span class="string">"z77z"</span>);</div><div class="line"> mpg.setPackageInfo(pc);</div><div class="line"> <span class="comment">// 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值</span></div><div class="line"> InjectionConfig cfg = <span class="keyword">new</span> InjectionConfig() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">initMap</span><span class="params">()</span> </span>{</div><div class="line"> Map<String, Object> map = <span class="keyword">new</span> HashMap<String, Object>();</div><div class="line"> map.put(<span class="string">"abc"</span>, <span class="keyword">this</span>.getConfig().getGlobalConfig().getAuthor() + <span class="string">"-mp"</span>);</div><div class="line"> <span class="keyword">this</span>.setMap(map);</div><div class="line"> }</div><div class="line"> };</div><div class="line"> mpg.setCfg(cfg);</div><div class="line"> <span class="comment">// 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/template 下面内容修改,</span></div><div class="line"> <span class="comment">// 放置自己项目的 src/main/resources/template 目录下, 默认名称一下可以不配置,也可以自定义模板名称</span></div><div class="line"> <span class="comment">// TemplateConfig tc = new TemplateConfig();</span></div><div class="line"> <span class="comment">// tc.setController("...");</span></div><div class="line"> <span class="comment">// tc.setEntity("...");</span></div><div class="line"> <span class="comment">// tc.setMapper("...");</span></div><div class="line"> <span class="comment">// tc.setXml("...");</span></div><div class="line"> <span class="comment">// tc.setService("...");</span></div><div class="line"> <span class="comment">// tc.setServiceImpl("...");</span></div><div class="line"> <span class="comment">// mpg.setTemplate(tc);</span></div><div class="line"> <span class="comment">// 执行生成</span></div><div class="line"> mpg.execute();</div><div class="line"> <span class="comment">// 打印注入设置</span></div><div class="line"> System.err.println(mpg.getCfg().getMap().get(<span class="string">"abc"</span>));</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>新建上面java文件,注释已经解释的很清楚了,这里就不多说,配置好后直接运行,就会得到相应模块代码,直接将其稍作修改放到项目中即可。</p>
</blockquote>
<h2 id="对插件生成的代码简要分析"><a href="#对插件生成的代码简要分析" class="headerlink" title="对插件生成的代码简要分析"></a>对插件生成的代码简要分析</h2><p>就拿生成的mapper文件做说明:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170122225056645?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="生成的mapper文件" title="">
</div>
<div class="image-caption">生成的mapper文件</div>
</figure>
<blockquote>
<p>如上图所示生成的mapper继承了一个类,是mybatisplus提供的,查看其源码可以发现,继承的类里面封装了一些常用的通用的增删改查的代码,还有对分页查询的处理。简化了开发的代码量,只需要专注于业务逻辑的编写和实现,源码如下:</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能</div><div class="line"> * </p></div><div class="line"> * <p></div><div class="line"> * 这个 Mapper 支持 id 泛型</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@author</span> hubin</div><div class="line"> * <span class="doctag">@Date</span> 2016-01-23</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BaseMapper</span><<span class="title">T</span>> </span>{</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 插入一条记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> entity</div><div class="line"> * 实体对象</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">insert</span><span class="params">(T entity)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 ID 删除</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> id</div><div class="line"> * 主键ID</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">deleteById</span><span class="params">(Serializable id)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 columnMap 条件,删除记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> columnMap</div><div class="line"> * 表字段 map 对象</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">deleteByMap</span><span class="params">(@Param(<span class="string">"cm"</span>)</span> Map<String, Object> columnMap)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 entity 条件,删除记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">delete</span><span class="params">(@Param(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 删除(根据ID 批量删除)</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> idList</div><div class="line"> * 主键ID列表</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">deleteBatchIds</span><span class="params">(List<? extends Serializable> idList)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 ID 修改</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> entity</div><div class="line"> * 实体对象</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">updateById</span><span class="params">(T entity)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 whereEntity 条件,更新记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> entity</div><div class="line"> * 实体对象</div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span></div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">update</span><span class="params">(@Param(<span class="string">"et"</span>)</span> T entity, @<span class="title">Param</span><span class="params">(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 ID 查询</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> id</div><div class="line"> * 主键ID</div><div class="line"> * <span class="doctag">@return</span> T</div><div class="line"> */</div><div class="line"> <span class="function">T <span class="title">selectById</span><span class="params">(Serializable id)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 查询(根据ID 批量查询)</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> idList</div><div class="line"> * 主键ID列表</div><div class="line"> * <span class="doctag">@return</span> List<T></div><div class="line"> */</div><div class="line"> <span class="function">List<T> <span class="title">selectBatchIds</span><span class="params">(List<? extends Serializable> idList)</span></span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 查询(根据 columnMap 条件)</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> columnMap</div><div class="line"> * 表字段 map 对象</div><div class="line"> * <span class="doctag">@return</span> List<T></div><div class="line"> */</div><div class="line"> <span class="function">List<T> <span class="title">selectByMap</span><span class="params">(@Param(<span class="string">"cm"</span>)</span> Map<String, Object> columnMap)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 entity 条件,查询一条记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> entity</div><div class="line"> * 实体对象</div><div class="line"> * <span class="doctag">@return</span> T</div><div class="line"> */</div><div class="line"> <span class="function">T <span class="title">selectOne</span><span class="params">(@Param(<span class="string">"ew"</span>)</span> T entity)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 Wrapper 条件,查询总记录数</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象</div><div class="line"> * <span class="doctag">@return</span> int</div><div class="line"> */</div><div class="line"> <span class="function">Integer <span class="title">selectCount</span><span class="params">(@Param(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 entity 条件,查询全部记录</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span> List<T></div><div class="line"> */</div><div class="line"> <span class="function">List<T> <span class="title">selectList</span><span class="params">(@Param(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 Wrapper 条件,查询全部记录</div><div class="line"> * </p></div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span> List<T></div><div class="line"> */</div><div class="line"> List<Map<String, Object>> selectMaps(<span class="meta">@Param</span>(<span class="string">"ew"</span>) Wrapper<T> wrapper);</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 Wrapper 条件,查询全部记录</div><div class="line"> * </p></div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span> List<Object></div><div class="line"> */</div><div class="line"> <span class="function">List<Object> <span class="title">selectObjs</span><span class="params">(@Param(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 entity 条件,查询全部记录(并翻页)</div><div class="line"> * </p></div><div class="line"> * </div><div class="line"> * <span class="doctag">@param</span> rowBounds</div><div class="line"> * 分页查询条件(可以为 RowBounds.DEFAULT)</div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类(可以为 null)</div><div class="line"> * <span class="doctag">@return</span> List<T></div><div class="line"> */</div><div class="line"> <span class="function">List<T> <span class="title">selectPage</span><span class="params">(RowBounds rowBounds, @Param(<span class="string">"ew"</span>)</span> Wrapper<T> wrapper)</span>;</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * <p></div><div class="line"> * 根据 Wrapper 条件,查询全部记录(并翻页)</div><div class="line"> * </p></div><div class="line"> *</div><div class="line"> * <span class="doctag">@param</span> rowBounds</div><div class="line"> * 分页查询条件(可以为 RowBounds.DEFAULT)</div><div class="line"> * <span class="doctag">@param</span> wrapper</div><div class="line"> * 实体对象封装操作类</div><div class="line"> * <span class="doctag">@return</span> List<Map<String, Object>></div><div class="line"> */</div><div class="line"> List<Map<String, Object>> selectMapsPage(RowBounds rowBounds, <span class="meta">@Param</span>(<span class="string">"ew"</span>) Wrapper<T> wrapper);</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<blockquote>
<p>对于其他的生成文件大家可以依照这个思路去查看其源码,结合之前传统的SSM开发,了解其原理。</p>
</blockquote>
<h2 id="创建springboot入口Application-java"><a href="#创建springboot入口Application-java" class="headerlink" title="创建springboot入口Application.java"></a>创建springboot入口Application.java</h2><blockquote>
<p>需要注意的地方我都写在注释里面了,这里我是把爬虫的启动也写在这个配置文件里面了,项目一启动就会执行爬虫。知道爬取的数据为空就停止爬取。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * springboot</div><div class="line"> * </div><div class="line"> * <span class="doctag">@author</span> z77z</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="comment">// 扫描指定包下面的mapper接口</span></div><div class="line"><span class="meta">@MapperScan</span>(<span class="string">"io.z77z.dao"</span>)</div><div class="line"></div><div class="line"><span class="comment">// 该 @SpringBootApplication 注解等价于以默认属性使用:</span></div><div class="line"><span class="comment">// @Configuration</span></div><div class="line"><span class="comment">// @EnableAutoConfiguration</span></div><div class="line"><span class="comment">// @ComponentScan</span></div><div class="line"></div><div class="line"><span class="meta">@SpringBootApplication</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Application</span> <span class="keyword">extends</span> <span class="title">SpringBootServletInitializer</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>{</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"> </div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> PictureService pictureService;</div><div class="line"> <span class="comment">//入口</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> SpringApplication.run(Application.class, args);</div><div class="line"> }</div><div class="line"> <span class="comment">//Java EE应用服务器配置,</span></div><div class="line"> <span class="comment">//如果要使用tomcat来加载jsp的话就必须继承SpringBootServletInitializer类并且重写其中configure方法</span></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> SpringApplicationBuilder <span class="title">configure</span> <span class="params">(SpringApplicationBuilder application)</span> </span>{</div><div class="line"> <span class="keyword">return</span> application.sources(Application.class);</div><div class="line"> }</div><div class="line"> <span class="comment">//springboot运行后此方法首先被调用</span></div><div class="line"> <span class="comment">//实现CommandLineRunner抽象类中的run方法</span></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="comment">//返回值</span></div><div class="line"> <span class="keyword">int</span> result = <span class="number">1</span>;</div><div class="line"> <span class="comment">//访问页码</span></div><div class="line"> Integer page = <span class="number">1</span>;</div><div class="line"> <span class="comment">//启动爬虫</span></div><div class="line"> System.out.println(<span class="string">"爬虫开始工作!"</span>);</div><div class="line"> <span class="keyword">while</span>(result==<span class="number">1</span>){</div><div class="line"> result = crawler(page.toString());</div><div class="line"> page+=<span class="number">1</span>;</div><div class="line"> <span class="keyword">if</span>(result==<span class="number">0</span>){</div><div class="line"> System.out.println(<span class="string">"爬虫运行结束!!"</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> </div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">crawler</span><span class="params">(String page)</span></span>{ </div><div class="line"> <span class="comment">//初始化返回值</span></div><div class="line"> <span class="keyword">int</span> result = <span class="number">1</span>;</div><div class="line"> <span class="comment">//网站首页地址</span></div><div class="line"> String homeUrl = <span class="string">"http://www.87g.com/"</span>;</div><div class="line"> <span class="comment">//接口地址</span></div><div class="line"> String url = <span class="string">"http://www.87g.com/index.php?m=content&c=content_ajax&a=picture_page&siteid=1&catid=35&page="</span>+page;</div><div class="line"> System.out.println(<span class="string">"当前爬取第"</span>+ page +<span class="string">"页数据"</span>);</div><div class="line"> <span class="comment">//访问接口,</span></div><div class="line"> JSONObject resultjson = CrawlerUtil.getReturnJson(url);</div><div class="line"> <span class="keyword">if</span>(resultjson!=<span class="keyword">null</span>){</div><div class="line"> <span class="comment">//获取其value值</span></div><div class="line"> Collection<Object> jsonList = resultjson.values();</div><div class="line"> <span class="keyword">for</span>(Object obj : jsonList){</div><div class="line"> BeautifulPictures beautifulPictures = JSON.parseObject(obj.toString(), BeautifulPictures.class);</div><div class="line"> String Keywords = beautifulPictures.getKeywords();</div><div class="line"> <span class="comment">//按map条件查询。判断是否已经爬过,没有就入库</span></div><div class="line"> Map<String, Object> map = <span class="keyword">new</span> HashMap<String, Object>();</div><div class="line"> map.put(<span class="string">"keywords"</span>, Keywords);</div><div class="line"> <span class="keyword">int</span> cont = beautifulPicturesService.selectByMap(map).size();</div><div class="line"> <span class="keyword">if</span>(cont==<span class="number">0</span>){</div><div class="line"> <span class="comment">//入库</span></div><div class="line"> beautifulPicturesService.insert(beautifulPictures);</div><div class="line"> <span class="comment">//访问链接获取document,并保存里面的图片</span></div><div class="line"> List<Picture> listPicture = CrawlerUtil.getArticleInfo(homeUrl+beautifulPictures.getUrl(),beautifulPictures);</div><div class="line"> <span class="keyword">for</span>(Picture picture : listPicture){</div><div class="line"> <span class="comment">//入库</span></div><div class="line"> pictureService.insert(picture);</div><div class="line"> }</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> System.out.println(homeUrl+beautifulPictures.getUrl()+<span class="string">"页面数据已经爬过了!!"</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }<span class="keyword">else</span>{</div><div class="line"> System.out.println(<span class="string">"爬取到"</span>+page+<span class="string">"页时没有数据了!!"</span>);</div><div class="line"> result = <span class="number">0</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="编写测试jsp和controller"><a href="#编写测试jsp和controller" class="headerlink" title="编写测试jsp和controller"></a>编写测试jsp和controller</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">%@</span> <span class="attr">page</span> <span class="attr">language</span>=<span class="string">"java"</span> <span class="attr">contentType</span>=<span class="string">"text/html; charset=UTF-8"</span></span></div><div class="line"> <span class="attr">pageEncoding</span>=<span class="string">"UTF-8"</span>%></div><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html; charset=UTF-8"</span>></span></div><div class="line"><span class="tag"><<span class="name">title</span>></span>Insert title here<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> helloJsp</div><div class="line"> <span class="tag"><<span class="name">hr</span>></span>${user}</div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">//如果是要返回jsp页面就必须要使用@Controller而不是@RestController</div><div class="line">@Controller</div><div class="line">@RequestMapping("/test")</div><div class="line">public class TestController {</div><div class="line"> </div><div class="line"> @Autowired</div><div class="line"> BeautifulPicturesService beautifulPicturesService;</div><div class="line"> </div><div class="line"> @RequestMapping("/test1") </div><div class="line"> public String view(Model model,Page<BeautifulPictures> page) {</div><div class="line"> Page<BeautifulPictures> pageList= beautifulPicturesService.selectPage(page);</div><div class="line"> model.addAttribute("user",JSON.toJSONString(pageList.getRecords()));</div><div class="line"> return "index";</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h2><p>直接运行Application.java启动项目,项目会运行在springboot内嵌的web容器中,本项目是使用的内嵌tomcat容器。运行成功后,在浏览器输入<a href="http://localhost:8080/test/test1?current=2&size=10" target="_blank" rel="external">http://localhost:8080/test/test1?current=2&size=10</a>(curren代表要获取的页码,size代表要获取的数据条数)就会出现下面效果:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170122231926662?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title="">
</div>
<div class="image-caption">这里写图片描述</div>
</figure>]]></content>
<summary type="html">
<blockquote>
<p>最近浏览很多博客,学习了不少新的知识,收获颇多,就想着能不能将新学的知识整合一下来练练手,提高自己撸代码搭框架的能力,还有就是给大家一个新年福利,爬一爬美女图片网站。上车请刷卡,哈哈。顺便就拿这爬来的数据作为基础数据来整合最近学习的框架。一劳多得。
</summary>
<category term="springboot" scheme="http://z77z.oschina.io/tags/springboot/"/>
<category term="爬虫" scheme="http://z77z.oschina.io/tags/%E7%88%AC%E8%99%AB/"/>
<category term="mybatisplus" scheme="http://z77z.oschina.io/tags/mybatisplus/"/>
</entry>
<entry>
<title>免费个人博客搭建教程(详细-图文)--Hexo+OSChina</title>
<link href="http://z77z.oschina.io/2017/01/14/%E5%85%8D%E8%B4%B9%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E6%95%99%E7%A8%8B%EF%BC%88%E8%AF%A6%E7%BB%86-%E5%9B%BE%E6%96%87%EF%BC%89--Hexo+OSChina/"/>
<id>http://z77z.oschina.io/2017/01/14/免费个人博客搭建教程(详细-图文)--Hexo+OSChina/</id>
<published>2017-01-14T09:49:58.000Z</published>
<updated>2017-01-14T14:09:26.591Z</updated>
<content type="html"><![CDATA[<blockquote>
<p> 本人作为一个屌丝程序员,<strong>年少无为,卖马为生</strong>,买不起服务器,买不起域名,但是又想拥有属于自己的博客网站,那就只有通过技术来实现这一切了。先上成果:<a href="http://z77z.oschina.io/">点击</a>,现在我把我自己搭建博客的过程共享出来,只要你按照步骤一步步走下去,一定会搭建成功,如果大家在搭建过程中遇到什么问题,欢迎在我的博客评论区留言,也欢迎大神进来,教我做码,带我装逼,带我飞。<strong><a href="http://z77z.oschina.io/">博客地址</a></strong>,JUST DO IT(<code>离开舒适区</code>)。</p>
</blockquote>
<hr>
<h2 id="使用工具介绍"><a href="#使用工具介绍" class="headerlink" title="使用工具介绍"></a>使用工具介绍</h2><ol>
<li><p><code>码云 Pages</code>:码云 Pages 是一个免费的静态网页托管服务,您可以使用码云 Pages 托管博客、项目官网等静态网页。<strong>这样就不用购买服务器和域名了</strong>,如果您使用过 <code>Github Pages</code> 那么您会很快上手使用码云的Pages服务。这里使用码云上的Pages 而不是用<code>Github Pages</code>,主要原因就是在国内没有VPN的话。。。你懂得!还有就是支持国产!链接:<a href="https://git.oschina.net/" target="_blank" rel="external">https://git.oschina.net/</a></p>
</li>
<li><p><code>Hexo</code>:Hexo是一个快速、简洁且高效的博客框架。Hexo使用 <code>Markdown</code>(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。使用者只需要专注于写博客而不需要关注网站是如何生成的。而且还有丰富的博客主题可以选择!链接:<a href="https://hexo.io/zh-cn/" target="_blank" rel="external">https://hexo.io/zh-cn/</a>(ps:这是国外网站,翻墙吧少年,需要<code>自由门</code> 翻墙软件可以在博客中联系我哦)</p>
</li>
</ol>
<h2 id="搭建环境"><a href="#搭建环境" class="headerlink" title="搭建环境"></a>搭建环境</h2><p> <strong>Node.js安装。</strong></p>
<blockquote>
<p>参考:<a href="http://www.runoob.com/nodejs/nodejs-install-setup.html" target="_blank" rel="external">http://www.runoob.com/nodejs/nodejs-install-setup.html</a>(ps:如果没有VPN的用户最好切换一下npm的源,不然后续的步骤可能会让你想砸电脑,安装完后,在命令窗口执行下面代码)</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm config set registry http://registry.cnpmjs.org #切换npm为淘宝镜像</div></pre></td></tr></table></figure>
<p> <strong>Git安装。</strong></p>
<blockquote>
<p>参考<a href="http://www.runoob.com/git/git-install-setup.html" target="_blank" rel="external">http://www.runoob.com/git/git-install-setup.html</a></p>
</blockquote>
<p><strong>Hexo安装。</strong>( ps:安装完Node.js和Git之后不需要配置些什么,只需要安装成功就是了,如果想明白原理的话可以深入的学习。)</p>
<p> 如果您的电脑中已经安装上述必备程序,那么恭喜您!接下来只需要使用 npm 即可完成 Hexo 的安装。打开命令窗口输入下面代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install -g hexo-cli</div></pre></td></tr></table></figure>
<p>安装成功后输入<code>hexo</code> 如果得到下面这个结果,恭喜你!安装成功!</p>
<p><img src="http://img.blog.csdn.net/20170114130729127?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="Hexo安装成功后效果"></p>
<p>Hexo的常用命令说明:</p>
<blockquote>
<p>参考:<a href="https://hexo.io/zh-cn/docs/commands.html" target="_blank" rel="external">https://hexo.io/zh-cn/docs/commands.html</a></p>
</blockquote>
<h2 id="本地运行Hexo"><a href="#本地运行Hexo" class="headerlink" title="本地运行Hexo"></a>本地运行Hexo</h2><p>安装 Hexo 完成后,请依次执行下列命令,Hexo 将会在指定文件夹中新建所需要的文件。将<code><folder></code> 替换成项目存放的文件夹目录,<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">hexo init <folder></div><div class="line">cd <folder></div><div class="line">npm install</div><div class="line">hexo generate</div></pre></td></tr></table></figure></p>
<p>新建完成后,指定文件夹的目录如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">.</div><div class="line">├── .deploy #需要部署的文件</div><div class="line">├── node_modules #Hexo插件</div><div class="line">├── public #生成的静态网页文件</div><div class="line">├── scaffolds #模板</div><div class="line">├── source #博客正文和其他源文件, 404 favicon CNAME 等都应该放在这里</div><div class="line">| ├── _drafts #草稿</div><div class="line">| └── _posts #文章</div><div class="line">├── themes #主题</div><div class="line">├── _config.yml #全局配置文件</div><div class="line">└── package.json</div></pre></td></tr></table></figure>
<p> 进行到这步后就可以先在本地运行下,看看效果了。执行下面命令:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">cd <folder> #切换到项目目录下</div><div class="line">npm install #install before start blogging</div><div class="line">hexo server #运行本地服务</div></pre></td></tr></table></figure>
<p>浏览器输入<a href="http://localhost:4000" target="_blank" rel="external">http://localhost:4000</a>就可以看到效果。如下:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114140920538?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="默认配置运行效果" title="">
</div>
<div class="image-caption">默认配置运行效果</div>
</figure>
<h2 id="修改Hexo的主题"><a href="#修改Hexo的主题" class="headerlink" title="修改Hexo的主题"></a>修改Hexo的主题</h2><p>当然,上面运行的只是Hexo官方默认的主题配置效果,想要个性一点?,下面我们就将他改造成自己想要的个性主题。</p>
<p>Hexo的主题都是一些前端民间高手写的模版,可以在官方收录的主题中去挑选:<a href="https://hexo.io/themes/" target="_blank" rel="external">https://hexo.io/themes/</a></p>
<p>看上一个主题后点击下图位置,进入下载主题文件。如下图:</p>
<p><img src="http://img.blog.csdn.net/20170114143254775?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt=""></p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114143625904?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>主题下载完成后,将主题文件解压到Hexo项目的thems文件夹下面(ps:我这里的项目目录是在桌面的Hexo文件夹里面。)</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114144033857?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>接下来就是修改Hexo的配置文件<code>_config.yml</code>,将里面<code>theme</code> 对应的值改为之前下载的主题的文件夹名字,本文里面下载的主题文件夹名字为<code>hexo-theme-smackdown-master</code>。</p>
<p>修改前</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114144752014?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="修改前" title="">
</div>
<div class="image-caption">修改前</div>
</figure>
<p>修改后</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114145129378?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="修改后" title="">
</div>
<div class="image-caption">修改后</div>
</figure>
<p><em>注意:这里“:”后面必须要有一个空格,而且这个空格要在英文输入法下,不然会报一些稀奇古怪的错。</em></p>
<p>按照之前步骤本地重新运行后,如下图:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114145727287?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="改变主题后效果" title="">
</div>
<div class="image-caption">改变主题后效果</div>
</figure>
<p>关于Hexo中<code>_config.yml</code> 文件的其他配置,大家可以参考官方的文档,这里就不多赘述了:</p>
<blockquote>
<p>文档链接 <a href="https://hexo.io/zh-cn/docs/configuration.html" target="_blank" rel="external">https://hexo.io/zh-cn/docs/configuration.html</a></p>
</blockquote>
<p>下载的每个主题中,有一个配置文件,名字也叫<code>_config.yml</code>,这里大家千万不要和之前Hexo根目录下的<code>_config.yml</code>搞混淆了,这里面配置的主要是些与主题相关的东西,比如一些文章阅读量,多说插件,cnzz站长工具等等的配置信息。一般里面也都有注释,这里就不赘述了,不懂得可以在我的博客问我。</p>
<p>一些主题在GitHub上面也都有主题安装的一些文档,写的都很详细。推荐一个主题的文档,结合官方的文档看完之后,基本上也就明白了。</p>
<blockquote>
<p>推荐一个主题的文档<a href="https://github.com/yscoder/hexo-theme-indigo/wiki" target="_blank" rel="external">https://github.com/yscoder/hexo-theme-indigo/wiki</a></p>
</blockquote>
<h2 id="编写博客文章"><a href="#编写博客文章" class="headerlink" title="编写博客文章"></a>编写博客文章</h2><p>如果你使用过<code>MarkDown</code> 来写博客文章的话,接下来就简单多了,没使用过也没关系,推荐大家一个编辑工具<code>马克飞象</code> 链接:<a href="https://maxiang.io/" target="_blank" rel="external">https://maxiang.io/</a>,在编辑器里面写好文章后,复制或另存为.md文件, 与普通的.md文件不同 要在文件开头添加下面代码</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">title: #文章标题</div><div class="line">date: #文章日期</div><div class="line">tags: #文章标签</div><div class="line">categories: #文章分类</div><div class="line">---</div></pre></td></tr></table></figure>
<p>在.md文件的开头添加上面代码,是为了让Hexo框架在生成网页的时候,设置相应的参数。例如下图所示:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114153926853?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>将写好的.md文件放入Hexo项目的<code>source</code> 目录下的<code>_posts</code> 文件夹中,可以看到里面有个<code>hello-world.md</code> 文件,这就是默认的文章。</p>
<p>重新在本地启动项目,访问就可以看到之前添加的文章。</p>
<h2 id="生成静态Html文件"><a href="#生成静态Html文件" class="headerlink" title="生成静态Html文件"></a>生成静态Html文件</h2><p>现在只能在本地启动项目然后通过本地地址访问博客网站,下面我们就可以利用Hexo生成静态Html,很简单,只需要在命令窗口执行下面代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">cd <folder> #切换到项目目录下</div><div class="line">hexo generate #生成静态文件到项目根目录的public文件夹中</div></pre></td></tr></table></figure>
<h2 id="发布静态Html文件到码云-Pages上"><a href="#发布静态Html文件到码云-Pages上" class="headerlink" title="发布静态Html文件到码云 Pages上"></a>发布静态Html文件到码云 Pages上</h2><p>注册一个码云帐号,并创建一个项目。如下图:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114160734101?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>创建完项目后得到项目的Https的地址后面要用。地址获取如下图</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114160903554?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>这里使用git将项目中<code>public</code> 文件夹下的文件管理起来,并推送到码云上。</p>
<p>这里借用一个插件来帮助我们完成,安装 hexo-deployer-git。安装代码如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install hexo-deployer-git --save</div></pre></td></tr></table></figure>
<p>配置项目根目录<code>_config.yml</code> 文件,修改<code>deploy</code> 的值,如下图:</p>
<p>修改前</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114163000889?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="修改前" title="">
</div>
<div class="image-caption">修改前</div>
</figure>
<p>修改后,注意<code>repo</code> 的地址是之前在码云上面创建醒目后获取的地址。</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114163013874?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="修改后" title="">
</div>
<div class="image-caption">修改后</div>
</figure>
<p>修改完后在命令窗口执行下面命令:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">cd <folder> #切换到项目目录下</div><div class="line">hexo deploy #一键部署功能</div></pre></td></tr></table></figure>
<p><strong>之后会弹出一个对话框,输入码云的帐号密码。</strong></p>
<p>部署成功之后,登录码云,查看之前创建的项目中出现了<code>public</code> 文件夹中的文件,这时候代表之前的部署是成功的。</p>
<p>然后如下图,启动码云的pages功能:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114164018356?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>访问链接:</p>
<figure class="image-bubble">
<div class="img-lightbox">
<div class="overlay"></div>
<img src="http://img.blog.csdn.net/20170114164154882?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjA5NTQ5NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" title="">
</div>
<div class="image-caption"></div>
</figure>
<p>这时候就看到之前和本地启动一样的效果了。博客部署完成,在这之后,只需要每次用马克飞象写好文章后,放入Hexo项目的<code>source</code> 目录下的<code>_posts</code> 文件夹中,在按照之前步骤更新博客就OK了。还可以随时切换博客的主题哦!</p>
]]></content>
<summary type="html">
<blockquote>
<p> 本人作为一个屌丝程序员,<strong>年少无为,卖马为生</strong>,买不起服务器,买不起域名,但是又想拥有属于自己的博客网站,那就只有通过技术来实现这一切了。先上成果:<a href="http://z77z.oschina.io/">
</summary>