好,我们往下看,接着就是应用我们实际项目里的自定义用户权限了
Java代码
<security:authentication-manager> <security:authentication-provider user-service-ref="customUserDetailsService"> <security:password-encoder ref="passwordEncoder" /> </security:authentication-provider> </security:authentication-manager> <!-- 对密码进行MD5编码 --> <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" /> <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />
首先是<security:authentication-manager>是指定我们自定义的身份验证策略,这里我们用customUserDetailsService这个bean,就是指向我们CustomUserDetailsService.java这个类。然后<security:password-encoder>指定我们密码使用MD5进行编码,调用Spring Security自带的MD5加密类。当然,还有加盐MD5或我们自己写的加密算法等安全性更加高的密码策略。这个按项目实际使用配置吧。
然后看到我们的CustomUserDetailsService.java
Java代码
package org.yzsoft.springmvcdemo.util; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.yzsoft.springmvcdemo.serviceimpl.UsersServiceImpl; import org.yzsoft.springmvcdemo.vo.TUsers; /** * 一个自定义的类用来和数据库进行操作. 即以后我们要通过数据库保存权限.则需要我们继承UserDetailsService * * @author * */ public class CustomUserDetailsService implements UserDetailsService { protected static Logger logger = Logger.getLogger("service");//log4j,不用解释了吧。。 @Autowired private UsersServiceImpl usersService; public UsersServiceImpl getUsersService() { return usersService; } public void setUsersService(UsersServiceImpl usersService) { this.usersService = usersService; } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { UserDetails user = null; try { // 搜索数据库以匹配用户登录名. // 我们可以通过dao使用Hibernate来访问数据库 System.out.println(username + " 用户页面输入的用户名"); TUsers tusers = this.usersService.findByUsername(username); System.out.println(tusers.getUsername() + " 数据库取出的用户名"); // Populate the Spring User object with details from the dbUser // Here we just pass the username, password, and access level // getAuthorities() will translate the access level to the correct // role type // 用户名、密码、是否启用、是否被锁定、是否过期、权限 user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole()))); } catch (Exception e) { logger.error("用户信息错误!"); throw new UsernameNotFoundException("异常处理:检索用户信息未通过!"); } return user; } /** * 获得访问角色权限列表 * * @param access * @return */ public Collection<GrantedAuthority> getAuthorities(Integer role) { System.out.println("取得的权限是 :" + role); List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); // 所有的用户默认拥有ROLE_USER权限 if (role == 0) { System.out.println("普通用户"); logger.debug("取得普通用户权限-->"); authList.add(new GrantedAuthorityImpl("ROLE_USERS")); } // 如果参数role为1.则拥有ROLE_ADMIN权限 if (role == 1) { logger.debug("取得ADMIN用户权限-->"); authList.add(new GrantedAuthorityImpl("ROLE_ADMIN")); } System.out.println(authList.size()+" 权限列表长度"); return authList; } }
这里就是把我们从数据库里面取得的用户权限和Spring Security的配置进行桥接,还记得上面配置文件里的ROLE_ADMIN吧,就是从这里来的,很奇怪的是,这个必须设置成ROLE_ 开头才有效。。郁闷。。。
这里我刚开始有一个疑惑,我们看这2句
Java代码
TUsers tusers = this.usersService.findByUsername(username); user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole())));
这里根本不需要用户输入的密码,只要了用户名,然后直接根据用户名去取权限,就直接设置进Spring Security的User对象里面去,我不禁一身冷汗,这不相当于说有了用户名就直接去查数据库么,而且是不用密码的。。。。
但经过查看官方文档和网上的解释,这才放心,原来是这样的,Spring Security的确是直接根据用户名去查,但是查得出来的Spring Security User对象之后,它会根据这个对象的属性值去数据库查询与这个对象匹配的数据,我们这里设置的是(用户名,密码,是否启用、是否被锁定、是否过期、权限。。。),那么如果数据库存在这个对象,就返回真,否则返回假,这样也就不用担心了,完全可靠。就是我们在前台要做好限制,不能给用户不输密码就访问, 不然挤爆你数据库连接。。。。。
最后登陆页面index.jsp
Java代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP ‘index.jsp‘ starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> 用户登陆 <br> ${SPRING_SECURITY_LAST_EXCEPTION.message} <form action="j_spring_security_check" method="post"> USERNAME:<input type="text" name="j_username" value="${sessionScope[‘SPRING_SECURITY_LAST_USERNAME‘]}" /><br/> PASSWORD:<input type="password" name="j_password" value="" /><br/> <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆(这个功能没有做的)<br/> <input type="submit"> </form> </body> </html>
这里我还是使用Spring Security默认的j_username和j_password,表单目标也用默认的j_spring_security_check,会默认跳到Spring Security进行拦截。其他的应该不用解释了吧。。。。
最后 ,我们整理一下Spring Security的整个控制过程:
——>用户登陆
——> <security:authentication-manager> 拦截
——>交给customUserDetailsService处理,并且声明密码采用MD5策略
——>根据输入的用户名去数据库查这条记录,验证身份
——>取出该条记录的用户权限(锁定、禁用、过期和实际权限等)
——>根据取得的权限列表去security:intercept-url匹配、授权,然后判断是否放行。
这就完成了一整个的权限控制流程。
接下来我们来测试一下看是否真的生效了:
1、测试匿名访问页面,直接地址栏访问:
2、普通用户登陆
3、然后访问后台管理页面,跳回了登陆页
4、管理员用户登陆
退出后成功跳转回登陆页,点击后退再执行其他操作,这时候session已经注销了的,不能执行,又跳回了登陆页。是我们想要的效果,OK,成功了。
好 了,Spring Security的简单使用就讲到这里,其实这只是Spring Security的一小部分,而且这里我还没有用权限表对用户权限进行专门的管理,很多东西还是用Spring Security 默认的,还有Spring Security CAS (单点登陆)以及更加高级的权限控制和更完善的Spring Security 配置,以后我们再慢慢去研究吧。发现Spring Security 这个技术不仅简化了我们的用户权限管理,要知道我们做管理系统的时候这是个大问题,也差不多颠覆了我一贯以来用户权限管理的观念,但是掌握了这种思维之 后,又发现,其实,程序并不是只有一种实现方式,它激发了我写程序时要去寻找多种解决方案的想法。学习的路上,就是要不断推翻自己固有的思维,去见识多种 新事物,才能有进步。