Spring技术黑幕——深入解析Spring架设与设计原理(6)Spring ACEGI

来源:互联网 发布:暴风影音mac历史版本 编辑:程序博客网 时间:2024/05/21 11:30

 Spring ACEGI
作为Spring丰富生态系统中的一个十分典型的使用,保险构架Spring ACEGI的施用是非曲直常宽泛的。固然它不属于Spring平台的范畴,但因为它建立在Spring的基础上,因而可以方便地与Spring运用集成,故而便利的为基于Spring的使用提供保险服务。
作为一个完整的Java EE保险运用解决方案,ACEGI能够为基于Spring构建的使用项目,提供全面的保险服务,它可以处置运用亟需的各种典型的保险需求;比如,用户的身份验证、用户受权,之类。ACEGI由于其优秀的兑现,而被Spring开发团队推荐作为Spring使用的通用保险构架,跟着Spring的宽泛传播而被广泛应用。在各种相关Spring的读物,文档和使用项目中,都可以看到它活泼的身形。

Spring ACEGI的根本兑现
至于ACEGI的根本设立,在这里就未几啰嗦了。我们关怀的是ACEGI是怎的兑现用户的保险需求的,例如最基本的用户印证,受权的工作原理和兑现。
在ACEGI配备中,是透过AuthenticationProcessingFilter的过滤效能来起步Web页面的用户应验兑现的。AuthenticationProcessingFilter过滤器的基类是AbstractProcessingFilter,在这个AbstractProcessingFilter的兑现中,可以看到印证进程的兑现模板,在这个兑现模板中,可以看到它定义了兑现证验的根本进程,如以次代码所示:

Java代码
1. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
2. throws IOException, ServletException {
3. //查验是否契合ServletRequest/SevletResponse的要求
4. if (!(request instanceof HttpServletRequest)) {
5. throw new ServletException("Can only process HttpServletRequest");
6. }
7.
8. if (!(response instanceof HttpServletResponse)) {
9. throw new ServletException("Can only process HttpServletResponse");
10. }
11.
12. HttpServletRequest httpRequest = (HttpServletRequest) request;
13. HttpServletResponse httpResponse = (HttpServletResponse) response;
14.
15. if (requiresAuthentication(httpRequest, httpResponse)) {
16. if (logger.isDebugEnabled()) {
17. logger.debug("Request is to process authentication");
18. }
19.//这边定义ACEGI中的Authentication对象,故此透过这个Authentication对象,来握有用户应验信息
20. Authentication authResult;
21.
22. try {
23. onPreAuthentication(httpRequest, httpResponse);
24.//具体会证历程嘱托给子类完成,例如经过AuthenticationProcessingFilter来完成基于Web页面的用户应验
25. authResult = attemptAuthentication(httpRequest);
26. } catch (AuthenticationException failed) {
27. // Authentication failed
28. unsuccessfulAuthentication(httpRequest, httpResponse, failed);
29.
30. return;
31. }
32.
33. // Authentication success
34. if (continueChainBeforeSuccessfulAuthentication) {
35. chain.doFilter(request, response);
36. }
37.//证验工作完成后的后续工作,跳转到呼应的页面,跳转的页面途径已经抓好了配备
38. successfulAuthentication(httpRequest, httpResponse, authResult);
39.
40. return;
41. }
42.
43. chain.doFilter(request, response);
44. }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//查验是否契合ServletRequest/SevletResponse的要求
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("Can only process HttpServletRequest");
}

if (!(response instanceof HttpServletResponse)) {
throw new ServletException("Can only process HttpServletResponse");
}

HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;

if (requiresAuthentication(httpRequest, httpResponse)) {
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
//这边定义ACEGI中的Authentication对象,故而透过这个Authentication对象,来怀有用户印证信息
Authentication authResult;

try {
onPreAuthentication(httpRequest, httpResponse);
//具体味证进程嘱托给子类完成,例如经过AuthenticationProcessingFilter来完成基于Web页面的用户证验
authResult = attemptAuthentication(httpRequest);
} catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(httpRequest, httpResponse, failed);

return;
}

// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
//证验工作完成后的后续工作,跳转到呼应的页面,跳转的页面途径已经办好了配备
successfulAuthentication(httpRequest, httpResponse, authResult);

return;
}

chain.doFilter(request, response);
}

在看到上边的对WEB页面请求的阻截后,处置开始转到ACEGI构架中后盾了,我们看到,完成证验工作的重要种在ACEGI中是AuthenticationManager。如以次代码所示:

Java代码
一.public final Authentication authenticate(Authentication authRequest)
2. throws AuthenticationException {
3. try {
4. /*doAuthentication是一个抽象步骤,由具体的AuthenticationManager兑现,故而完成证验工作。传到的参数是一个Authentication对象,在这个对象中已经打包了从HttpServletRequest中失去的用户名和密码,这些信息都是在页面登录时用户输入的*/
5. Authentication authResult = doAuthentication(authRequest);
6. copyDetails(authRequest, authResult);
7. return authResult;
8. } catch (AuthenticationException e) {
9. e.setAuthentication(authRequest);
10. throw e;
11. }
12.}
13.
14./**
15. * Copies the authentication details from a source Authentication object to a destination one, provided the
16. * latter does not already have one set.
17. */
18.private void copyDetails(Authentication source, Authentication dest) {
19. if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
20. AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
21.
22. token.setDetails(source.getDetails());
23. }
24.}
25.protected abstract Authentication doAuthentication(Authentication authentication)
26. throws AuthenticationException;
public final Authentication authenticate(Authentication authRequest)
throws AuthenticationException {
try {
/*doAuthentication是一个抽象步骤,由具体的AuthenticationManager兑现,故而完成证验工作。传到的参数是一个Authentication对象,在这个对象中已经打包了从HttpServletRequest中失去的用户名和密码,这些信息都是在页面登录时用户输入的*/
Authentication authResult = doAuthentication(authRequest);
copyDetails(authRequest, authResult);
return authResult;
} catch (AuthenticationException e) {
e.setAuthentication(authRequest);
throw e;
}
}

/**
* Copies the authentication details from a source Authentication object to a destination one, provided the
* latter does not already have one set.
*/
private void copyDetails(Authentication source, Authentication dest) {
if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;

token.setDetails(source.getDetails());
}
}
protected abstract Authentication doAuthentication(Authentication authentication)
throws AuthenticationException;


而读取用户信息的操作,我们举大家伙儿已经很熟悉的DaoAuthenticationProvider作为事例。可以看到,在配备的JdbcDaoImpl中,定义了读取用户数据的操作,如以次代码所示:

Java代码
1. public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT username,password,enabled FROM users WHERE username = ?";
2. public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?";
3. public UserDetails loadUserByUsername(String username)
4. throws UsernameNotFoundException, DataAccessException {
5.//施用Spring JDBC SqlMappingQuery来完成用户信息的查询
6. List users = usersByUsernameMapping.execute(username);
7.//依据输入的用户名,没查询到呼应的用户信息
8. if (users.size() == 零) {
9. throw new UsernameNotFoundException("User not found");
10. }
11.//如其查询到一个用户列表,应用列表中的第一个作为查询失去的用户
12. UserDetails user = (UserDetails) users.get(零); // contains no GrantedAuthority[]
13.//运用Spring JDBC SqlMappingQuery来完成用户权限信息的查询
14. List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());
15.
16. addCustomAuthorities(user.getUsername(), dbAuths);
17.
18. if (dbAuths.size() == 零) {
19. throw new UsernameNotFoundException("User has no GrantedAuthority");
20. }
21.
22. GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);
23.
24. String returnUsername = user.getUsername();
25.
26. if (!usernameBasedPrimaryKey) {
27. returnUsername = username;
28. }
29. //依据查询的用户信息和权限信息,结构User对象回来
30. return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);
31. }
public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT username,password,enabled FROM users WHERE username = ?";
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?";
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
//应用Spring JDBC SqlMappingQuery来完成用户信息的查询
List users = usersByUsernameMapping.execute(username);
//依据输入的用户名,没查询到呼应的用户信息
if (users.size() == 零) {
throw new UsernameNotFoundException("User not found");
}
//如其查询到一个用户列表,应用列表中的第一个作为查询失去的用户
UserDetails user = (UserDetails) users.get(零); // contains no GrantedAuthority[]
//运用Spring JDBC SqlMappingQuery来完成用户权限信息的查询
List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());

addCustomAuthorities(user.getUsername(), dbAuths);

if (dbAuths.size() == 零) {
throw new UsernameNotFoundException("User has no GrantedAuthority");
}

GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);

String returnUsername = user.getUsername();

if (!usernameBasedPrimaryKey) {
returnUsername = username;
}
//依据查询的用户信息和权限信息,结构User对象回到
return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);
}


ACEGI受权器的兑现
ACEGI就像一位称职的,负责安全保卫工作的警卫员,在它的工作中,非但要对来访人员的身份进行检查(透过口令分辨身份),还可以依据判别出来的身份,授予其不同权限的钥匙,故此可以去打开不同的门禁,失去不平级别的服务。从这点上看,与在这个场景中的“警卫员”人员担当的角色同样,ACEGI在Spring使用系统中,起到的也是相仿的捍卫系统保险的功用,而证验和受权,便诀别对应于警卫员判别上访者身份和为其授予权限的历程。
为用户受权是由AccessDecisionManager受权器来完成的,受权的进程,在受权器的decide步骤中兑现,这个decide步骤是AccessDecisionManger定义的一个接口步骤,经过这个接口步骤,可以对应好几个具体的受权器兑现,至于受权器完成决议计划的守则兑现,在这里,我们以AffirmativeBased受权器为例,见见在AffirmativeBased受权器中,兑现的一票决议受权守则是怎么完成的,这个兑现进程,如以次代码所示:

Java代码
1. public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
2. throws AccessDeniedException {
3. //获得配备投票器的迭代器,可以用于遍历全部的投票器
4. Iterator iter = this.getDecisionVoters().iterator();
5. int deny = 零;
6.
7. while (iter.hasNext()) {
8. //获得现阶段投票器的投票结果
9. AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
10. int result = voter.vote(authentication, object, config);
11. //对投票结果开展处置,如果是碰到ACCESS_GRANT的结果,受权直连通过
12. //要不然,合计ACCESS_DENIED的投票票数
13. switch (result) {
14. case AccessDecisionVoter.ACCESS_GRANTED:
15. return;
16.
17. case AccessDecisionVoter.ACCESS_DENIED:
18. deny++;
19.
20. break;
21.
22. default:
23. break;
24. }
25. }
26.//如果有反对票,那么回绝受权
27. if (deny > 零) {
28. throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
29. "Access is denied"));
30. }
31.// 这边对弃权票开展处置,见见是全是弃权票的决议状况,默许是不通过,这种处置景况,是由allowIfAllAbstainDecisions变量来统制的
32. // To get this far, every AccessDecisionVoter abstained
33. checkAllowIfAllAbstainDecisions();
34. }
public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
throws AccessDeniedException {
//获得配备投票器的迭代器,可以用以遍历全部的投票器
Iterator iter = this.getDecisionVoters().iterator();
int deny = 零;

while (iter.hasNext()) {
//获得现阶段投票器的投票结果
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, object, config);
//对投票结果进展处置,如果是碰到ACCESS_GRANT的结果,受权直连通过
//不然,总计ACCESS_DENIED的投票票数
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;

case AccessDecisionVoter.ACCESS_DENIED:
deny++;

break;

default:
break;
}
}
//如果有反对票,那么回绝受权
if (deny > 零) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}
// 这边对弃权票开展处置,见见是全是弃权票的决议状况,默许是不通过,这种处置状况,是由allowIfAllAbstainDecisions变量来统制的
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}


可以看到,在ACEGI的构架兑现中,施用的保险需求治理,重要是由过滤器、印证器、用户数据提供器、受权器、投票器,这几个根本模块的合作一行完成的。这几个根本模块的关系,描写出了ACEGI内部架设的基本情况,也是我们基于ACEGI兑现Spring保险使用,急需着重点关切的地方。
本文来源:我的异常网

原创粉丝点击