千家信息网

shiro教程(4)-shiro与项目集成开发

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,1 shiro与项目集成开发1.1 shiro与spring web项目整合shiro与springweb项目整合在"基于url拦截实现的工程"基础上整合,基于url拦截实现的工程的技术架构是spri
千家信息网最后更新 2025年01月20日shiro教程(4)-shiro与项目集成开发

1 shiro与项目集成开发

1.1 shirospring web项目整合

shirospringweb项目整合在"基于url拦截实现的工程"基础上整合,基于url拦截实现的工程的技术架构是springmvc+mybatis,整合注意两点:

1shirospring整合

2、加入shiroweb应用的支持

1.1.1 取消原springmvc认证和授权拦截器

去掉springmvc.xml中配置的LoginInterceptorPermissionInterceptor拦截器。

1.1.2 加入shirojar

1.1.3 web.xml添加shiro Filter

[html] view plain copy print?

  1. <filter>

  2. <filter-name>shiroFilterfilter-name>

  3. <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>

  4. <init-param>

  5. <param-name>targetFilterLifecycleparam-name>

  6. <param-value>trueparam-value>

  7. init-param>

  8. filter>

  9. <filter-mapping>

  10. <filter-name>shiroFilterfilter-name>

  11. <url-pattern>/*url-pattern>

  12. filter-mapping>



1.1.4 applicationContext-shiro.xml

[html] view plain copy print?

  1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

  2. <property name="securityManager" ref="securityManager" />

  3. <property name="loginUrl" value="/login.action" />

  4. <property name="unauthorizedUrl" value="/refuse.jsp" />

  5. <property name="filters">

  6. <map>

  7. <entry key="authc" value-ref="formAuthenticationFilter" />

  8. map>

  9. property>

  10. <property name="filterChainDefinitions">

  11. <value>

  12. /loginsubmit.action = authc

  13. /logout.action = logout

  14. /refuse.jsp = anon

  15. /item/list.action = roles[item],authc

  16. /js/** anon

  17. /p_w_picpaths/** anon

  18. /styles/** anon

  19. /** = user

  20. value>

  21. property>

  22. bean>

  23. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

  24. <property name="realm" ref="userRealm" />

  25. bean>

  26. <bean id="userRealm" class="cn.itcast.ssm.realm.CustomRealm1">

  27. bean>

  28. <bean id="formAuthenticationFilter"

  29. class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">

  30. <property name="usernameParam" value="usercode" />

  31. <property name="passwordParam" value="password" />

  32. <property name="loginUrl" value="/loginsubmit.action" />

  33. bean>



securityManager:这个属性是必须的。

loginUrl:没有登录认证的用户请求将跳转到此地址,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的"/login.jsp"页面。

unauthorizedUrl:没有权限默认跳转的页面。

1.1.5 使用shiro注解授权

springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置权限:

[html] view plain copy print?

  1. <aop:config proxy-target-class="true">aop:config>

  2. <bean

  3. class="

  4. org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">

  5. <property name="securityManager" ref="securityManager" />

  6. bean>



修改Controller代码,在方法上添加授权注解,如下:

[java] view plain copy print?

  1. // 查询商品列表

  2. @RequestMapping("/queryItem")

  3. @RequiresPermissions("item:query")

  4. public ModelAndView queryItem() throws Exception {



上边代码@RequiresPermissions("item:query")表示必须拥有"item:query"权限方可执行。

其它的方法参考示例添加注解

1.1.6 自定义realm

realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。

[java] view plain copy print?

  1. public class CustomRealm1 extends AuthorizingRealm {

  2. @Autowired

  3. private SysService sysService;

  4. @Override

  5. public String getName() {

  6. return "customRealm";

  7. }

  8. // 支持什么类型的token

  9. @Override

  10. public boolean supports(AuthenticationToken token) {

  11. return token instanceof UsernamePasswordToken;

  12. }

  13. // 认证

  14. @Override

  15. protected AuthenticationInfo doGetAuthenticationInfo(

  16. AuthenticationToken token) throws AuthenticationException {

  17. // 从token中 获取用户身份信息

  18. String username = (String) token.getPrincipal();

  19. // 拿username从数据库中查询

  20. // ....

  21. // 如果查询不到则返回null

  22. if (!username.equals("zhang")) {// 这里模拟查询不到

  23. return null;

  24. }

  25. // 获取从数据库查询出来的用户密码

  26. String password = "123";// 这里使用静态数据模拟。。

  27. // 根据用户id从数据库取出菜单

  28. //...先用静态数据

  29. List menus = new ArrayList();;

  30. SysPermission sysPermission_1 = new SysPermission();

  31. sysPermission_1.setName("商品管理");

  32. sysPermission_1.setUrl("/item/queryItem.action");

  33. SysPermission sysPermission_2 = new SysPermission();

  34. sysPermission_2.setName("用户管理");

  35. sysPermission_2.setUrl("/user/query.action");

  36. menus.add(sysPermission_1);

  37. menus.add(sysPermission_2);

  38. // 构建用户身体份信息

  39. ActiveUser activeUser = new ActiveUser();

  40. activeUser.setUserid(username);

  41. activeUser.setUsername(username);

  42. activeUser.setUsercode(username);

  43. activeUser.setMenus(menus);

  44. // 返回认证信息由父类AuthenticatingRealm进行认证

  45. SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(

  46. activeUser, password, getName());

  47. return simpleAuthenticationInfo;

  48. }

  49. // 授权

  50. @Override

  51. protected AuthorizationInfo doGetAuthorizationInfo(

  52. PrincipalCollection principals) {

  53. // 获取身份信息

  54. ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();

  55. //用户id

  56. String userid = activeUser.getUserid();

  57. // 根据用户id从数据库中查询权限数据

  58. // ....这里使用静态数据模拟

  59. List permissions = new ArrayList();

  60. permissions.add("item:query");

  61. permissions.add("item:update");

  62. // 将权限信息封闭为AuthorizationInfo

  63. SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

  64. for (String permission : permissions) {

  65. simpleAuthorizationInfo.addStringPermission(permission);

  66. }

  67. return simpleAuthorizationInfo;

  68. }

  69. }



1.1.7 登录

[java] view plain copy print?

  1. //用户登陆页面

  2. @RequestMapping("/login")

  3. public String login()throws Exception{

  4. return "login";

  5. }

  6. // 用户登陆提交

  7. @RequestMapping("/loginsubmit")

  8. public String loginsubmit(Model model, HttpServletRequest request)

  9. throws Exception {

  10. // shiro在认证过程中出现错误后将异常类路径通过request返回

  11. String exceptionClassName = (String) request

  12. .getAttribute("shiroLoginFailure");

  13. if (UnknownAccountException.class.getName().equals(exceptionClassName)) {

  14. throw new CustomException("账号不存在");

  15. } else if (IncorrectCredentialsException.class.getName().equals(

  16. exceptionClassName)) {

  17. throw new CustomException("用户名/密码错误");

  18. } else{

  19. throw new Exception();//最终在异常处理器生成未知错误

  20. }

  21. }



1.1.8 首页

由于session由shiro管理,需要修改首页的controller方法:

[java] view plain copy print?

  1. //系统首页

  2. @RequestMapping("/first")

  3. public String first(Model model)throws Exception{

  4. //主体

  5. Subject subject = SecurityUtils.getSubject();

  6. //身份

  7. ActiveUser activeUser = (ActiveUser) subject.getPrincipal();

  8. model.addAttribute("activeUser", activeUser);

  9. return "/first";

  10. }



1.1.9 退出

由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可。

[html] view plain copy print?

  1. /logout.action = logout



1.1.10 无权限refuse.jsp

当用户无操作权限,shiro将跳转到refuse.jsp页面。

参考:applicationContext-shiro.xml

1.2 realm连接数据库

1.2.1 添加凭证匹配器

添加凭证匹配器实现md5加密校验。

修改applicationContext-shiro.xml

[html] view plain copy print?

  1. <bean id="credentialsMatcher"

  2. class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">

  3. <property name="hashAlgorithmName" value="md5" />

  4. <property name="hashIterations" value="1" />

  5. bean>

  6. <bean id="userRealm" class="cn.itcast.ssm.realm.CustomRealm1">

  7. <property name="credentialsMatcher" ref="credentialsMatcher" />

  8. bean>



1.2.2 realm代码

修改realm代码从数据库中查询用户身份信息和权限信息,将sysService注入realm

[java] view plain copy print?

  1. public class CustomRealm1 extends AuthorizingRealm {

  2. @Autowired

  3. private SysService sysService;

  4. @Override

  5. public String getName() {

  6. return "customRealm";

  7. }

  8. // 支持什么类型的token

  9. @Override

  10. public boolean supports(AuthenticationToken token) {

  11. return token instanceof UsernamePasswordToken;

  12. }

  13. @Override

  14. protected AuthenticationInfo doGetAuthenticationInfo(

  15. AuthenticationToken token) throws AuthenticationException {

  16. // 从token中获取用户身份

  17. String usercode = (String) token.getPrincipal();

  18. SysUser sysUser = null;

  19. try {

  20. sysUser = sysService.findSysuserByUsercode(usercode);

  21. } catch (Exception e) {

  22. // TODO Auto-generated catch block

  23. e.printStackTrace();

  24. }

  25. // 如果账号不存在

  26. if (sysUser == null) {

  27. throw new UnknownAccountException("账号找不到");

  28. }

  29. // 根据用户id取出菜单

  30. List menus = null;

  31. try {

  32. menus = sysService.findMenuList(sysUser.getId());

  33. } catch (Exception e) {

  34. // TODO Auto-generated catch block

  35. e.printStackTrace();

  36. }

  37. // 用户密码

  38. String password = sysUser.getPassword();

  39. //盐

  40. String salt = sysUser.getSalt();

  41. // 构建用户身体份信息

  42. ActiveUser activeUser = new ActiveUser();

  43. activeUser.setUserid(sysUser.getId());

  44. activeUser.setUsername(sysUser.getUsername());

  45. activeUser.setUsercode(sysUser.getUsercode());

  46. activeUser.setMenus(menus);

  47. SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(

  48. activeUser, password, ByteSource.Util.bytes(salt),getName());

  49. return simpleAuthenticationInfo;

  50. }

  51. @Override

  52. protected AuthorizationInfo doGetAuthorizationInfo(

  53. PrincipalCollection principals) {

  54. //身份信息

  55. ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();

  56. //用户id

  57. String userid = activeUser.getUserid();

  58. //获取用户权限

  59. List permissions = null;

  60. try {

  61. permissions = sysService.findSysPermissionList(userid);

  62. } catch (Exception e) {

  63. // TODO Auto-generated catch block

  64. e.printStackTrace();

  65. }

  66. //构建shiro授权信息

  67. SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

  68. for(SysPermission sysPermission:permissions){

  69. simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());

  70. }

  71. return simpleAuthorizationInfo;

  72. }

  73. }



1.3 缓存

shiro每个授权都会通过realm获取权限信息,为了提高访问速度需要添加缓存,第一次从realm中读取权限数据,之后不再读取,这里ShiroEhcache整合。

1.3.1 添加Ehcachejar

1.3.2 配置

applicationContext-shiro.xml中配置缓存管理器。

[html] view plain copy print?

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

  2. <property name="realm" ref="userRealm" />

  3. <property name="sessionManager" ref="sessionManager" />

  4. <property name="cacheManager" ref="cacheManager"/>

  5. bean>

  6. <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">

  7. bean>



1.4 session管理

applicationContext-shiro.xml中配置sessionManager

[html] view plain copy print?

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

  2. <property name="realm" ref="userRealm" />

  3. <property name="sessionManager" ref="sessionManager" />

  4. bean>

  5. <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">

  6. <property name="globalSessionTimeout" value="600000"/>

  7. <property name="deleteInvalidSessions" value="true"/>

  8. bean>



1.5 验证码

1.5.1 自定义FormAuthenticationFilter

需要在验证账号和名称之前校验验证码。

[java] view plain copy print?

  1. public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

  2. protected boolean onAccessDenied(ServletRequest request,

  3. ServletResponse response, Object mappedValue) throws Exception {

  4. // 校验验证码

  5. // 从session获取正确的验证码

  6. HttpSession session = ((HttpServletRequest)request).getSession();

  7. //页面输入的验证码

  8. String randomcode = request.getParameter("randomcode");

  9. //从session中取出验证码

  10. String validateCode = (String) session.getAttribute("validateCode");

  11. if (!randomcode.equals(validateCode)) {

  12. // randomCodeError表示验证码错误

  13. request.setAttribute("shiroLoginFailure", "randomCodeError");

  14. //拒绝访问,不再校验账号和密码

  15. return true;

  16. }

  17. return super.onAccessDenied(request, response, mappedValue);

  18. }

  19. }



1.5.2 修改FormAuthenticationFilter配置

修改applicationContext-shiro.xml中对FormAuthenticationFilter的配置。

[html] view plain copy print?

  1. <bean id="formAuthenticationFilter"

  2. class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">

  3. 改为

  4. <bean id="formAuthenticationFilter"

  5. class="cn.itcast.ssm.shiro.MyFormAuthenticationFilter">



1.5.3 登陆页面

添加验证码:

[html] view plain copy print?

  1. <TR>

  2. <TD>验证码:TD>

  3. <TD><input id="randomcode" name="randomcode" size="8" /> <img

  4. id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""

  5. width="56" height="20" align='absMiddle' /> <a

  6. href=javascript:randomcode_refresh()>刷新a>TD>

  7. TR>



1.5.4 配置validatecode.jsp匿名访问

修改applicationContext-shiro.xml

1.6 记住我

用户登陆选择"自动登陆"本次登陆成功会向cookie写身份信息,下次登陆从cookie中取出身份信息实现自动登陆。

1.6.1 用户身份实现java.io.Serializable接口

cookie记录身份信息需要用户身份信息对象实现序列化接口,如下:

1.6.2 配置

[html] view plain copy print?

  1. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

  2. <property name="realm" ref="userRealm" />

  3. <property name="sessionManager" ref="sessionManager" />

  4. <property name="cacheManager" ref="cacheManager"/>

  5. <property name="rememberMeManager" ref="rememberMeManager"/>

  6. bean>

  7. <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">

  8. <property name="cookie" ref="rememberMeCookie" />

  9. bean>

  10. <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">

  11. <constructor-arg value="rememberMe" />

  12. <property name="maxAge" value="2592000" />

  13. bean>


修改formAuthenticationFitler添加页面中"记住我checkbox"的input名称:

[html] view plain copy print?

  1. <bean id="formAuthenticationFilter"

  2. class="cn.itcast.ssm.shiro.MyFormAuthenticationFilter">

  3. <property name="usernameParam" value="usercode" />

  4. <property name="passwordParam" value="password" />

  5. <property name="rememberMeParam" value="rememberMe"/>

  6. <property name="loginUrl" value="/loginsubmit.action" />

  7. bean>



1.6.3 登陆页面

login.jsp中添加"记住我"checkbox

[html] view plain copy print?

  1. <TR>

  2. <TD>TD>

  3. <TD>

  4. <input type="checkbox" name="rememberMe" />自动登陆

  5. TD>

  6. TR>



2 附:

2.1 shiro过虑器

过滤器简称

对应的Java类

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

roles例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

perms例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"]当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中methodpostgetdelete等。

port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议httphttps等,serverName是你访问的host,8081url配置里port的端口,queryString

是你访问的url里的?后面的参数。

authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

注:

anonauthcBasicauchcuser是认证过滤器,

permsrolessslrestport是授权过滤器

2.2 shirojsp标签

Jsp页面添加:

<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>

标签名称

标签条件(均是显示标签内容)

登录之后

不在登录状态时

用户在没有RememberMe时

用户在RememberMe时

在有abc或者123角色时

拥有角色abc

没有角色abc

拥有权限资源abc

没有abc权限资源

显示用户身份名称

显示用户身份中的属性值


0