J% O* d: x3 M1 A F( E% U通过SecurityUtils.getSubject(),就可以获取当前Subject。Subject是应用中用户的一个特定安全的缩影,虽然感觉上直接使用User会更贴切,但是实际上它的意义远远超过了User。而且每个应用程序都会有自己的用户以及框架,我们可不想和它们混淆在一起,况且Subject就是安全领域公认的名词。OK,我们继续。 8 |, u2 m) }% c ) W; e9 E. K( D: P" n6 W在单应用系统中,调用getSubject()会返回一个Subject,它是位于应用程序中特定位置的用户信息;在服务器中运行的情况下(比如web应用),getSubject会返回一个位于当前线程或请求中的用户信息。 现在你已经得到了Subject对象,那么用它可以做什么呢?2 X* L- B* |* O* c) S, e
. u# y) s E- O5 q7 ASession session = currentUser.getSession(); 7 \. f4 L8 g& D6 X- @ . V5 ?4 L8 r# Y% K& z, ^; gsession.setAttribute( "someKey", "aValue" ); - l% g* }$ b9 N: @& c, F9 ]" O$ X# W X& d" E* f% I, z
这个Session对象是Shiro中特有的对象,它和我们经常使用的HttpSession非常相似,但还提供了额外的东西,其中与HttpSession最大的不同就是Shiro中的Session不依赖HTTP环境(换句话说,可以在非HTTP 容器下运行)。 & z, Q7 M* X2 [3 g6 m % i# w6 S0 L+ T: K8 ^7 [如果将Shiro部署在web应用程序中,那么这个Session就是基于HttpSession的。但是像QuickStart示例那样,在非web环境下使用,Shiro则默认使用EnterpriseSessionManagment。也就是说,不论在应用中的任何一层使用同样的API,却不需要考虑部署环境,这一优点为应用打开一个全新的世界,因为应用中要获取Session对象再也不用依赖于HttpSession或者EJB的会话Bean。而且任何客户端技术都可以共享session 数据。2 |2 @: [4 T H \! D
+ r5 i! ~/ g" F& a8 c G$ {2 F现在你可以得到当前Subject和它的Session对象。那么我们如何验证比如角色和权限这些东西呢?- P1 T4 b" Y/ e6 Y5 z5 g' Z/ R
1 y7 x7 z9 u7 F' H2 g
很简单,可以通过已得到的user对象进行验证。Subject对象代表当前用户,但是,谁才是当前用户呢?他们可是匿名用户啊。也就是说,必须登录才能获取到当前用户。没问题,这样就可以搞定:% y4 @1 ]4 e, `4 J: B
& c4 J$ m+ ^; j/ _$ l9 m
if ( !currentUser.isAuthenticated() ) {: \' e. ~4 c; {- ~1 [, F
- x+ @" L: N! r- q
//collect user principals and credentials in a gui specific manner% }% H6 ~2 P8 l; B G
0 r$ J5 e, o( ?2 H+ g6 q/ Q
//such as username/password html form, X509 certificate, OpenID, etc.' q, ?, c# b! j, |! K
3 L/ Y- |' V3 x9 o7 D0 _7 J3 A
//We'll use the username/password example here since it is the most common. ' n* c) R/ ^3 Y9 t9 V3 K- a3 _3 x8 h
//(do you know what movie this is from? ;)) q0 T3 }. V! r
$ Z) p! }/ N6 y3 p) r
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");) K" s) M2 x. ~
2 D" \, A3 u6 f# F$ [; N; M+ U//this is all you have to do to support 'remember me' (no config - built in!):7 ^' G5 L$ k8 v
& T. l( P& Y, V1 P4 _+ a
token.setRememberMe(true); . z/ I; M2 A2 o+ C( G, w: K ' ]8 Z: H5 B: d& {; d& _$ i- ^currentUser.login(token);- w7 ~) K2 V- `/ t9 w) e1 n6 ?
D, a2 ~, l% U% Q+ F
} & a# B# X+ Q# `, u- N# ~- g. L
就是这样,太简单了吧! " ~# R. {+ f# H- y! [- w$ G7 p# c6 j0 k5 `, s
那登录失败了怎么处理呢?可以通过捕获各类异常,根据不同类型的异常做出不同的处理: ' a7 h' @9 |0 \1 J 6 \7 [: q, ?4 m/ ]7 ytry { ' s- V+ w% D, j$ o2 c; S) M8 Y0 b9 }0 @8 f* P& ^2 K/ g/ c
currentUser.login( token );+ ~) Y( I! |: H D
6 H1 }6 T7 T. Y' F @* ~, r3 ~% J//if no exception, that's it, we're done! + ^0 x: k, P# J( N# b U1 k& L. t2 ?4 W/ k, r X
} catch ( UnknownAccountException uae ) {, d, O9 ]3 C1 I( m4 a: G# F! `
# a7 O, |2 Y8 ^
//username wasn't in the system, show them an error message? # o" U- z: F0 q# N5 W9 Z9 k2 ]% I3 y, o1 q% b4 r
} catch ( IncorrectCredentialsException ice ) { , G2 ~. S' H( k, C- W0 F6 u8 C0 x$ Y# @6 s3 S) h" v
//password didn't match, try again? 9 Y$ g+ N! P" a& n ^6 R 8 h% b$ v7 U8 A+ @- E( _. C6 p} catch ( LockedAccountException lae ) {* Z7 r7 f+ n" l' c3 I4 m
" V8 v( s& p% Z1 e2 J }- @//account for that username is locked - can't login. Show them a message? . o( O/ A" Z1 m9 _1 [4 @# q : x$ b& a7 [2 R( @) ]} ; |8 `7 U5 n7 a& P }# v; v ]* M7 F! ]( t; Z& o( y
... more types exceptions to check if you want ... & y6 b# V2 } C3 Q; O; D: \0 U$ f6 r5 e
} catch ( AuthenticationException ae ) { ; \4 p! q; {/ v; _ 9 _6 b6 t# I$ S//unexpected condition - error?' H1 H- a5 Z1 _6 s" ?0 {5 m
) y0 m+ T- n% W# U- B}' `: j' I3 n& g- `
( p) j% g3 n' f" X. A" t可以捕获Shiro提供的各种异常,也可以抛出自定义类异常用于处理Shiro未考虑到的情况。预知详情,可以去了解AuthenticationException JavaDoc。+ ~; ] B0 X- a3 i9 W6 P
. N5 J+ m( o) @, k+ b提示:最安全的做法是将登录失败的消息告知用户,你总不会帮助攻击者入侵你的系统吧!! \3 n4 k: T" p1 c1 M
8 d( x* ~* Z: R3 c9 }
OK,现在已经拥有一个登录用户了,我们还能做点儿什么呢? 0 [* D: a3 V2 \ e & l# T- N2 H6 `! a3 |* F8 i比方说,他们是谁: - V% C1 d5 G( o1 }3 [0 }& ^% k+ g+ l 7 ~4 b( h$ Y$ Z//print their identifying principal (in this case, a username): : j' a: ]* g! X$ [7 O- Z ' j0 ~2 d0 Q3 o- ?. zlog.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." ); . W* H, I& a5 i9 L# F4 N+ ^6 N $ F. u7 \$ I3 z/ t/ Z也可以判断用户是否拥有特定的角色:. a' d) |9 {" D* w
. N; B9 ?2 \ P5 qlog.info("May the Schwartz be with you!" );4 o4 l; P4 _/ G2 V& ^
* i9 c2 u. Q' _& p4 B3 \; }0 V} else { 1 R$ D; B+ c- H6 C0 R # b) ^% m2 K! {/ C: h7 i* E% n0 ^log.info( "Hello, mere mortal." );2 }, U. Y) T3 i+ [- k
) E9 J! ]( h/ |- D
}4 g5 d5 Y- R1 C6 J: l
/ H1 Q5 E- S6 t& _
还可以判断用户是否对特定某实体有操作权限:: L' P3 {! |5 {5 `+ D) f9 w7 ~
- |% B, U2 x& l4 n. U8 I, mif ( currentUser.isPermitted( "lightsaber:weild" ) ) {' @' c2 E* C* q9 Y& s% o) l
- I7 Z1 N/ R4 u3 E6 Alog.info("You may use a lightsaber ring. Use it wisely."); ; G2 g/ d+ l$ g3 V: o2 d' Q; X/ f 2 I3 s( u5 {) s- z} else { ' M3 U# y- O4 a; s. a: k6 b w+ g. ~5 P$ w( ^log.info("Sorry, lightsaber rings are for schwartz masters only."); * m3 l+ I0 J0 h8 q8 j) e* ~4 ~; e6 o0 ^9 w, t4 Y- @1 V
}" c+ e/ X5 D1 M0 {4 K
" u: E6 a- l" p9 O' w2 h当然,还可以进行功能强大的实例级别的权限验证。通过它可以判断用户是否有访问特定类型实例的权限: 9 a; G3 | g0 h7 X/ m T. l" i X+ r% f" A4 D! t; [. Q
if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {/ G) }; h# f, C- a K
! z$ v! x d9 X+ B! ~3 v0 L
log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +"Here are the keys - have fun!");( l8 K4 i, t5 w$ l! i
$ w' J# |$ w: S( o} else {' O9 ~; j( J9 B9 t
, V- {9 |1 \: h" C7 Y9 s5 ilog.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");1 q8 [* d8 c1 L+ d# R
( ^. |/ a9 ~; H; A7 D
}& X; N1 v' l1 A* d
3 n! I" u" U+ i小菜一碟,对吧。 ; n- h# [% E0 @: }9 D- y$ M1 V: L* }3 B- h- {% C
最后,当用户使用完毕,还可以退出应用。, Q+ z* m3 i' w
! }3 V4 r) A" e; @8 l+ s
currentUser.logout(); //removes all identifying information and invalidates their session too.: b8 B7 U( o: M; k
' V! j/ n- {, n; t这些就是使用Apache Shiro开发应用的核心了,当然,Apache Shiro已将将很多复杂的东西封装在内部了,但是现在它就是这么简单。/ H6 k' ^. L- U$ b
: Z' N( T+ q- H) x/ \# D4 d1 V你会有疑问吧,用户登录时,谁负责把用户信息(用户名、密码、角色、权限等)取出来,还有运行时,谁负责安全认证呢?当然由你决定了啊。通过将一个实现了Shiro中的Realm的Reaml配置到Shiro中即可。 $ @. b+ @8 ?! d6 |' ^3 l) s2 j$ \" S7 v/ V E1 h
至于如何配置很大程度上取决于你的运行时环境,比如在单应用、web应用、基于spring或JEE 容器的应用或者组合模式中使用Shiro,配置都有所不同。如何配置已经超出QuickStart示例的范围,因为它的主要目的是帮助你熟悉Shiro的API和概念。2 w' E# H3 w8 z. N* [7 F/ U
8 @8 q) X$ h ]& U, P, a& e如果想进一步了解Shiro,可以看看Authentication Guide和Authorization Guide。也可以查看其他文档(特别是Reference Manual),这里可以解决你的各种疑问。# F2 V/ H5 t+ Q* W
* ~# |7 I) v5 s, c5 F