shiro教学(一首歌的时候教你使用Shiro)
shiro教学(一首歌的时候教你使用Shiro)Subject subject=SecurityUtils.getSubject(); // 获取Subject单例对象SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManagerUsernamePasswordToken token =newUsernamePasswordToken(username password);token可以理解为用户令牌,登录的过程被抽象为Shiro验证令牌是否具有合法身份以及相关权限。(2)执行登陆动作
一、架构
要学习如何使用Shiro必须先从它的架构谈起,作为一款安全框架Shiro的设计相当精妙。Shiro的应用不依赖任何容器,它也可以在JavaSE下使用。但是最常用的环境还是JavaEE。下面以用户登录为例:
java
(1)使用用户的登录信息创建令牌
UsernamePasswordToken token =newUsernamePasswordToken(username password);
token可以理解为用户令牌,登录的过程被抽象为Shiro验证令牌是否具有合法身份以及相关权限。
(2)执行登陆动作
SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManager
Subject subject=SecurityUtils.getSubject(); // 获取Subject单例对象
subject.login(token); // 登陆
Shiro的核心部分是SecurityManager,它负责安全认证与授权。Shiro本身已经实现了所有的细节,用户可以完全把它当做一个黑盒来使用。SecurityUtils对象,本质上就是一个工厂类似spring中的ApplicationContext。Subject是初学者比较难于理解的对象,很多人以为它可以等同于User,其实不然。Subject中文翻译:项目,而正确的理解也恰恰如此。它是你目前所设计的需要通过Shiro保护的项目的一个抽象概念。通过令牌(token)与项目(subject)的登陆(login)关系,Shiro保证了项目整体的安全。
java
(5)获取用户的角色和权限信息
说了这么多才到我们的重点Realm,如果你已经理解了Shiro对于用户匹配和注册加密的全过程,真正理解Realm的实现反而比较简单。我们还得回到上文提及的两个非常类似的对象AuthorizationInfo和AuthenticationInfo。因为Realm就是提供这两个对象的地方。
publicclassUserRealmextendsAuthorizingRealm {//用户对应的角色信息与权限信息都保存在数据库中,通过UserService获取数据privateUserService userService =newUserServiceImpl();/*** 提供用户信息返回权限信息*/@OverrideprotectedAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username=(String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=newSimpleAuthorizationInfo();//根据用户名查询当前用户拥有的角色Set roles =userService.findRoles(username);
Set roleNames =newHashSet();for(Role role : roles) {
roleNames.add(role.getRole());
}//将角色名称提供给infoauthorizationInfo.setRoles(roleNames);//根据用户名查询当前用户权限Set permissions =userService.findPermissions(username);
Set permissionNames =newHashSet();for(Permission permission : permissions) {
permissionNames.add(permission.getPermission());
}//将权限名称提供给infoauthorizationInfo.setStringPermissions(permissionNames);returnauthorizationInfo;
}/*** 提供账户信息返回认证信息*/@OverrideprotectedAuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throwsAuthenticationException {
String username=(String) token.getPrincipal();
User user=userService.findByUsername(username);if(user ==null) {//用户名不存在抛出异常thrownewUnknownAccountException();
}if(user.getLocked() == 0) {//用户被管理员锁定抛出异常thrownewLockedAccountException();
}
SimpleAuthenticationInfo authenticationInfo=newSimpleAuthenticationInfo(user.getUsername()
user.getPassword() ByteSource.Util.bytes(user.getCredentialsSalt()) getName());returnauthenticationInfo;
}
}
根据Shiro的设计思路,用户与角色之前的关系为多对多,角色与权限之间的关系也是多对多。在数据库中需要因此建立5张表,分别是用户表(存储用户名,密码,盐等)、角色表(角色名称,相关描述等)、权限表(权限名称,相关描述等)、用户-角色对应中间表(以用户ID和角色ID作为联合主键)、角色-权限对应中间表(以角色ID和权限ID作为联合主键)。具体dao与service的实现本文不提供。总之结论就是,Shiro需要根据用户名和密码首先判断登录的用户是否合法,然后再对合法用户授权。而这个过程就是Realm的实现过程。
(6)会话
用户的一次登录即为一次会话,Shiro也可以代替Tomcat等容器管理会话。目的是当用户停留在某个页面长时间无动作的时候,再次对任何链接的访问都会被重定向到登录页面要求重新输入用户名和密码而不需要程序员在Servlet中不停的判断Session中是否包含User对象。启用Shiro会话管理的另一个用途是可以针对不同的模块采取不同的会话处理。以淘宝为例,用户注册淘宝以后可以选择记住用户名和密码。之后再次访问就无需登陆。但是如果你要访问支付宝或购物车等链接依然需要用户确认身份。当然,Shiro也可以创建使用容器提供的Session最为实现。
三、与SpringMVC集成
有了注册模块和Realm模块的支持,下面就是如何与SpringMVC集成开发。有过框架集成经验的同学一定知道,所谓的集成基本都是一堆xml文件的配置,Shiro也不例外。
(1)配置前端过滤器
先说一个题外话,Filter是过滤器,interceptor是拦截器。前者基于回调函数实现,必须依靠容器支持。因为需要容器装配好整条FilterChain并逐个调用。后者基于代理实现,属于AOP的范畴。
如果希望在WEB环境中使用Shiro必须首先在web.xml文件中配置
Shiro_Project
index.jsp
SpringMVCorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc.xml1true
SpringMVC/
org.springframework.web.context.ContextLoaderListener
org.springframework.web.util.Log4jConfigListener
contextConfigLocationclasspath:spring.xml classpath:spring-shiro-web.xml
log4jConfigLoactionclasspath:log4j.properties
shiroFilterorg.springframework.web.filter.DelegatingFilterProxytruetargetFilterLifecycletrue
shiroFilter/*
熟悉Spring配置的同学可以重点看有绿字注释的部分,这里是使Shiro生效的关键。由于项目通过Spring管理,因此所有的配置原则上都是交给Spring。DelegatingFilterProxy的功能是通知Spring将所有的Filter交给ShiroFilter管理。
接着在classpath路径下配置spring-shiro-web.xml文件
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">/authc/admin = roles[admin]
/authc/** = authc
/** = anon
需要注意filterChainDefinitions过滤器中对于路径的配置是有顺序的,当找到匹配的条目之后容器不会再继续寻找。因此带有通配符的路径要放在后面。三条配置的含义是: /authc/admin需要用户有用admin权限、/authc/**用户必须登录才能访问、/**其他所有路径任何人都可以访问。
说了这么多,大家一定关心在Spring中引入Shiro之后到底如何编写登录代码呢。
@ControllerpublicclassLoginController {
@AutowiredprivateUserService userService;
@RequestMapping("login")publicModelAndView login(@RequestParam("username") String username @RequestParam("password") String password) {
UsernamePasswordToken token=newUsernamePasswordToken(username password);
Subject subject=SecurityUtils.getSubject();try{
subject.login(token);
}catch(IncorrectCredentialsException ice) {//捕获密码错误异常ModelAndView mv =newModelAndView("error");
mv.addObject("message" "password error!");returnmv;
}catch(UnknownAccountException uae) {//捕获未知用户名异常ModelAndView mv =newModelAndView("error");
mv.addObject("message" "username error!");returnmv;
}catch(ExcessiveAttemptsException eae) {//捕获错误登录过多的异常ModelAndView mv =newModelAndView("error");
mv.addObject("message" "times error");returnmv;
}
User user=userService.findByUsername(username);
subject.getSession().setAttribute("user" user);returnnewModelAndView("success");
}
}
登录完成以后,当前用户信息被保存进Session。这个Session是通过Shiro管理的会话对象,要获取依然必须通过Shiro。传统的Session中不存在User对象。
@Controller
@RequestMapping("authc")publicclassAuthcController {///authc/** = authc 任何通过表单登录的用户都可以访问@RequestMapping("anyuser")publicModelAndView anyuser() {
Subject subject=SecurityUtils.getSubject();
User user= (User) subject.getSession().getAttribute("user");
System.out.println(user);returnnewModelAndView("inner");
}///authc/admin = user[admin] 只有具备admin角色的用户才可以访问,否则请求将被重定向至登录界面@RequestMapping("admin")publicModelAndView admin() {
Subject subject=SecurityUtils.getSubject();
User user= (User) subject.getSession().getAttribute("user");
System.out.println(user);returnnewModelAndView("inner");
}
}
四、总结
Shiro是一个功能很齐全的框架,使用起来也很容易,但是要想用好却有相当难度,需要交流的同学可以给我留言。如果大家感觉我写的还可以,也希望能给我一些反馈意见