shiro可以跟springMVC很好的集成。
但是对于shiro集成struts2的资料比较少。
容易遇到注解失效等问题。
因为很多老的项目可能用的是Spring+ sturts框架。
如果我们使用shiro跟sturts集成的话 需要用到sturts的注解。
这样其实struts也失去了它跳转配置的便利。
所以 考虑了之后 还是觉得 使用springMVC框架与shiro集成。
我现在有一个spirngMVC的框架,在此基础上进行shiro的添加测试。
用户分为两种用户normal,manager,有五个模块:结果查看模块,用户数据生成单选模块,用户数据生成批量模块,用户权限管理模块,操作日志查看模块。
normal用户:
结果查看模块resultlist,
用户数据生成单选模块parseResultUserlist,parseResultAdd,
用户数据生成批量模块parseResultUserlistMulti,parseResultAddMulti
manager用户:
结果查看模块resultlist,
用户数据生成单选模块parseResultUserlist,parseResultAdd,
用户数据生成批量模块parseResultUserlistMulti,parseResultAddMulti,
用户权限管理模块usermanage,
操作日志查看模块。loglist
根据shiro的模型 我们需要给User加上角色role和权限permission。因为我用的mongodb数据库是非关系型的数据库。我们把role和permission直接加在User对象里。
User.java
package com.test.web.support.shiro; import java.util.ArrayList; import java.util.List; import org.apache.shiro.authc.credential.DefaultPasswordService; import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import com.test.domain.entity.User; import com.test.domain.repository.UserReposity; public class UserAdd { public static void main(String[] args) { final String[] paths = new String[] { "src/main/resource/com/test/web/conf/mongodb/spring-mongodb.xml"}; final ApplicationContext context = new FileSystemXmlApplicationContext( paths); // ClassPathXmlApplicationContext final UserReposity processor = (UserReposity) context .getBean("userReposity"); User user=new User(); String username="test"; String password="123"; user.setUsername(username); String encrypted = new SimpleHash("MD5",password,null,1).toHex(); //这里的加密方式要与配置对应 //<property name="hashAlgorithmName" value="md5"/>指定hash算法为MD5; 无默认值,必须指定为MD5或者SHA-1等 //<property name="hashIterations" value="1"/>指定散列次数为1次;默认为1 // <property name="storedCredentialsHexEncoded" value="true"/>指定Hash散列值使用Hex加密存储。value="false"表明hash散列值用用Base64-encoded存储。默认为true user.setPassword(encrypted); List<String> permissionList=new ArrayList<String>(); List<String> roleList=new ArrayList<String>(); permissionList.add("parseResultAdd"); permissionList.add("parseResultAddMulti"); permissionList.add("resultlist"); roleList.add("normal"); user.setPermissionList(permissionList); user.setRoleList(roleList); processor.saveObject(user); } }
同时在数据库中中添加相关的user数据
我的mongodb数据添加两个用户如下:
{ "_id" : ObjectId("558915caa668aa7b08eb1197"), "_class" : "com.test.domain.entity.User", "username" : "admin", "password" : "202cb962ac59075b964b07152d234b70", "permissionList" : ["parseResultAdd", "parseResultAddMulti", "usermanage", "loglist", "resultlist"], "roleList" : ["admin"] } { "_id" : ObjectId("55891777a668e496191c6eb1"), "_class" : "com.test.domain.entity.User", "username" : "test", "password" : "202cb962ac59075b964b07152d234b70", "permissionList" : ["parseResultAdd", "parseResultAddMulti", "resultlist"], "roleList" : ["normal"] }
"password" : "202cb962ac59075b964b07152d234b70"对应密码为123。
这里采用了shiro自带的加密方式。详情看下面的代码。
package com.test.web.support.shiro; import java.util.ArrayList; import java.util.List; import org.apache.shiro.authc.credential.DefaultPasswordService; import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import com.test.domain.entity.User; import com.test.domain.repository.UserReposity; public class UserAdd { public static void main(String[] args) { final String[] paths = new String[] { "src/main/resource/com/test/web/conf/mongodb/spring-mongodb.xml"}; final ApplicationContext context = new FileSystemXmlApplicationContext( paths); // ClassPathXmlApplicationContext final UserReposity processor = (UserReposity) context .getBean("userReposity"); User user=new User(); String username="admin"; String password="123"; user.setUsername(username); String encrypted = new SimpleHash("MD5",password,null,1).toHex(); //这里的加密方式要与配置对应 //<property name="hashAlgorithmName" value="md5"/>指定hash算法为MD5; 无默认值,必须指定为MD5或者SHA-1等 //<property name="hashIterations" value="1"/>指定散列次数为1次;默认为1 // <property name="storedCredentialsHexEncoded" value="true"/>指定Hash散列值使用Hex加密存储。value="false"表明hash散列值用用Base64-encoded存储。默认为true user.setPassword(encrypted); List<String> permissionList=new ArrayList<String>(); List<String> roleList=new ArrayList<String>(); permissionList.add("parseResultAdd"); permissionList.add("parseResultAddMulti"); permissionList.add("usermanage"); permissionList.add("loglist"); permissionList.add("resultlist"); roleList.add("admin"); user.setPermissionList(permissionList); user.setRoleList(roleList); processor.saveObject(user); } }
String encrypted = new SimpleHash("MD5",password,null,1).toHex();
使用shiro注册增加用户时的加密方式要与自己的设置想匹配。
spring-shiro.xml中配置为:
<bean id="mongoRealm" class="com.test.web.support.shiro.MyShiro"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> </bean> </property> </bean>验证时为:
添加后如图所示:
因为我们的是maven工程,所以把shiro相关的包写入pom.xml文件。
在pom.xml中添加
<!--Apache Shiro所需的jar包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.2</version> </dependency>
完整版的pom.xml太长,这里就不给出了,可下载源码查看
在web.xml里添加shiro 的配置
shiro的filter应该放在struts2的 filter的上面
<!-- Shiro配置 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <filter> <filter-name>encodingFilter</filter-name> <filter-class>com.test.web.servlet.filter.EncodingFilter</filter-class> </filter> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-web.xml </param-value> </context-param> <welcome-file-list> <welcome-file></welcome-file> </welcome-file-list> <session-config> <cookie-config> <name>_sid</name> </cookie-config> <tracking-mode>COOKIE</tracking-mode> </session-config> <error-page> <error-code>404</error-code> <location>/404</location> </error-page> <error-page> <exception-type>javax.servlet.ServletException</exception-type> <location>/error</location> </error-page> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <page-encoding>UTF-8</page-encoding> <scripting-invalid>false</scripting-invalid> </jsp-property-group> </jsp-config> </web-app>
主要是扩展AuthorizingRealm, 实现在数据库中查询是否有该帐号密码,实现验证。
我新建一个MyShiro的class
MyShiro.java
package com.test.web.support.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import com.test.domain.entity.User; import com.test.domain.repository.UserReposity; public class MyShiro extends AuthorizingRealm{ @Autowired UserReposity userReposity; /** * 权限认证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录时输入的用户名 String loginName=(String) principalCollection.fromRealm(getName()).iterator().next(); //到数据库查是否有此对象 User user=userReposity.findByName(loginName); if(user!=null){ //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission) SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); //用户的角色集合 info.addRoles(user.getRoleList()); //用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的一行可以不要 info.addStringPermissions(user.getPermissionList()); return info; } return null; } /** * 登录认证; */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authenticationToken) throws AuthenticationException { //UsernamePasswordToken对象用来存放提交的登录信息 UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken; //查出是否有此用户 User user=userReposity.findByName(token.getUsername()); if(user!=null){ //若存在,将此用户存放到登录认证info中 return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } return null; } }
同时还应配置mongodb的访问xml和userReposity的bean定义,这里不给出了,详情可下载源码
主要有两部分
一是对数据库具体访问的接口实现
二是 spring-mongodb.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xmlns:repository="http://www.springframework.org/schema/data/repository" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.5.xsd "> <bean id="userReposity" class="com.test.domain.repository.impl.UserReposityImpl" parent="mongoTemplate"> </bean> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg name="mongo" ref="mongo" /> <constructor-arg name="databaseName" value="shiro_test"/> </bean> <bean id="mongo" class="org.springframework.data.mongodb.core.MongoFactoryBean"> <property name="host" value="127.0.0.1"/> <property name="port" value="27017"/> </bean> </beans>
HomeController.java
package com.test.web.controller; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import com.test.domain.entity.User; public class HomeController { @RequestMapping(value="/login",method=RequestMethod.GET) public String loginForm(Model model){ model.addAttribute("user", new User()); return "/login"; } @RequestMapping(value="/login",method=RequestMethod.POST) public String login(User user,BindingResult bindingResult,RedirectAttributes redirectAttributes){ try { if(bindingResult.hasErrors()){ return "/login"; } //使用权限工具进行用户登录,登录成功后跳到shiro配置的successUrl中,与下面的return没什么关系! SecurityUtils.getSubject().login(new UsernamePasswordToken(user.getUsername(), user.getPassword())); return "/home"; } catch (AuthenticationException e) { redirectAttributes.addFlashAttribute("message","用户名或密码错误"); return "redirect:/login"; } } @RequestMapping(value="/logout",method=RequestMethod.GET) public String logout(RedirectAttributes redirectAttributes ){ //使用权限管理工具进行用户的退出,跳出登录,给出提示信息 SecurityUtils.getSubject().logout(); redirectAttributes.addFlashAttribute("message", "您已安全退出"); return "redirect:/login"; } @RequestMapping("/403") public String unauthorizedRole(){ return "/403"; } }
三个页面放在views文件夹中 login.jsp,home.jsp,403.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'MyJsp.jsp' starting page</title> </head> <body> <h1>登录页面----${message }</h1> <form:form action="/login" commandName="user" method="post"> 用户名:<form:input path="username"/> <form:errors path="username" cssClass="error"/> <br/> 密 码:<form:password path="password"/> <form:errors path="password" cssClass="error" /> <br/> <form:button name="button">submit</form:button> </form:form> </body> </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>用户列表</title> </head> <body> <h1>${message }</h1> <h1>用户列表--<a href="/user/add">添加用户</a>---<a href="./logout">退出登录</a> </h1> <h2>权限列表----<shiro:principal/>-当前登录用户名 <br></h2> <shiro:authenticated>用户已经登录显示此内容<br></shiro:authenticated> <shiro:hasRole name="admin">admin角色登录显示此内容<br></shiro:hasRole> <shiro:hasRole name="normal">normal角色登录显示此内容<br></shiro:hasRole> <shiro:hasAnyRoles name="normal,admin">**normal or admin 角色用户登录显示此内容**<br></shiro:hasAnyRoles> <shiro:hasPermission name="loglist">loglist权限用户显示此内容<br></shiro:hasPermission> <shiro:lacksPermission name="loglist"> 不具有loglist权限的用户显示此内容 <br></shiro:lacksPermission> <script type="text/javascript" src="http://cdn.staticfile.org/jquery/1.9.1/jquery.min.js"></script> <script> $(function(){ $(".del").click(function(){ var id=$(this).attr("ref"); $.ajax({ type:"delete", url:"/user/del/"+id, success:function(e){ } }); }); }); </script> </body> </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>权限错误</title> </head> <body> <h1>对不起,您没有权限请求此连接!</h1> </body> </html>
spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- ~ Licensed to the Apache Software Foundation (ASF) under one ~ or more contributor license agreements. See the NOTICE file ~ distributed with this work for additional information ~ regarding copyright ownership. The ASF licenses this file ~ to you under the Apache License, Version 2.0 (the ~ "License"); you may not use this file except in compliance ~ with the License. You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, ~ software distributed under the License is distributed on an ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ~ KIND, either express or implied. See the License for the ~ specific language governing permissions and limitations ~ under the License. --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!--<bean id="iniRealm" class="org.apache.shiro.realm.text.IniRealm"> <constructor-arg name="resourcePath" value="classpath:com/test/web/conf/shiro/shiro.ini"></constructor-arg> </bean>--> <bean id="mongoRealm" class="com.test.web.support.shiro.MyShiro"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> </bean> </property> </bean> <!-- securityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="rememberMeManager"> <bean class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie"> <bean class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg name="name" value="RememberMe" /> <property name="maxAge" value="604800" /> </bean> </property> </bean> </property> <!-- <property name="realm" ref="iniRealm" /> --> <property name="realm" ref="mongoRealm" /> </bean> <!-- shiroFilter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/" /> <property name="unauthorizedUrl" value="/login" /> <property name="filters"> <map> <entry key="authc"> <bean class="com.test.web.support.shiro.AjaxCompatibleAuthenticationFilter"></bean> </entry> </map> </property> <property name="filterChainDefinitions"> <value> /login = anon <!-- anon表示此地址不需要任何权限即可访问 --> <!-- /static/**=anon --> <!-- perms[user:query]表示访问此连接需要权限为user:query的用户 --> <!-- /user=perms[user:query] --> <!-- roles[manager]表示访问此连接需要用户的角色为manager --> <!-- /user/add=roles[manager] --> <!-- /user/del/**=roles[admin] --> <!-- /user/edit/**=roles[manager] --> <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login--> <!-- /** = authc --> </value> </property> </bean> </beans>
启动后分别用admin和test账户登录可以看到根据角色和权限已经能显示不同的内容
realm如果不想用数据库,也可以用文本形式配置,如下
spring-shiro.xml中注释mongoRealm,启用iniRealm
<bean id="iniRealm" class="org.apache.shiro.realm.text.IniRealm"> <constructor-arg name="resourcePath" value="classpath:com/test/web/conf/shiro/shiro.ini"></constructor-arg> </bean> <!-- <bean id="mongoRealm" class="com.test.web.support.shiro.MyShiro"> <property name="credentialsMatcher"> <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> </bean> </property> </bean> --> <!-- securityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="rememberMeManager"> <bean class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie"> <bean class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg name="name" value="RememberMe" /> <property name="maxAge" value="604800" /> </bean> </property> </bean> </property> <property name="realm" ref="iniRealm" /> <!--<property name="realm" ref="mongoRealm" /> --> </bean>
新建shiro.ini
shiro.ini 里是帐号密码和角色
[users] admin = 123, admin test = 123, normal [roles] normal= * admin = *当然还可以有其它配置
源码下载:
springshiro