书接上回 构建 struts2 spring3 mybatis 的maven项目 构建 pom.xml
继续在原有框架下 融合shiro ,具体shiro是啥 这里就不解释了,恩 反正功能挺强大的
本着先会用再深入的原则,还是尝试着将shiro融入框架中
0 首先上下这个项目的整体结构图
1 在导入shiro的jar包 在pom.xml中添加shiro的配置
... <shiro.version>1.2.1</shiro.version> ... <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> </dependencies>
2 在web.xml中导入 shiro的过滤器
<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>shiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
这里要说明一下
shiro的过滤器是前置过滤器,需要添加在struts2的前面,如果放在struts2之后会报错
然后是在spring的过滤配置中添加spring-shiro的配置文件
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext-*.xml </param-value> </context-param>
配置文件为applicationContext-shiro.xml, 因为这里用了通配符 所以不用修改
3 然后 添加spring-shiro的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans...> <description>Shiro安全配置 来源于: http://shiro.apache.org/spring.html </description> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> </bean> <bean id="shiroDbRealm" class="lqb.shiro.ShiroDbRealm" /> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login_loginPage.do" /> <!-- 没有权限或者失败后跳转的页面 --> <property name="successUrl" value="/login_home.do" /> <property name="unauthorizedUrl" value="/other_error.do"/> <property name="filterChainDefinitions"> <value> /login_loginPage.do = anon /login_login.do = anon /login_home.do=authc /login_hello.do=authc /t1/**=roles[aa],perms[aaa] /t2/**=roles[bb],perms[baaa] /t3/**=roles[dd] </value> </property> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- AOP式方法级权限检查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> </beans>
这里需要就是下 filterChainDefinitions 的value
key 是 对应的跳转路径 这里都是指定的struts2的跳转 可以匹配通配符 *
value 是对应的过滤权限
anon 不需要验证
authc 需要登录验证
roles[aa] 角色验证 中括号内为指定的角色
perms[aaa] 权限验证 中括号内卫指定的权限
4 添加shiro的缓存配置文件
<ehcache> <diskStore path="java.io.tmpdir/shiro-spring-sample"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <cache name="shiro-activeSessionCache" maxElementsInMemory="10000" eternal="true" overflowToDisk="true" diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/> <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization" maxElementsInMemory="100" eternal="false" timeToLiveSeconds="600" overflowToDisk="false"/> </ehcache>
5 角色 权限 实现
因为只是一个demo 所以就没有弄角色表和权限表 只是模拟了一下 用户--角色--权限 的5表结构
用户是查的表 角色和权限只是假实现
6 修改 struts.xml
<struts> <!-- 全局包设置 --> <package name="defalutGlobal" namespace="/" extends="json-default"> </package> <!-- 自定义开发包 --> <package name="myDefault" extends="defalutGlobal"> <!--登录Action --> <action name="login_*" class="loginAction" method="{1}" > <result name="loginPage">WEB-INF/pages/login.html</result> <result name="loginPageForm">WEB-INF/pages/login2.html</result> <result name="home">WEB-INF/pages/home.html</result> <result name="hello">WEB-INF/pages/hello.html</result> <result name="success" type="json"> <param name="root">jsonResult</param> </result> </action> <action name="other_*" class="otherAction" method="{1}"> <result name="error">WEB-INF/pages/other/error.html</result> </action> </package> <package name="t1" extends="defalutGlobal" namespace="/t1"> <action name="t1_*" class="test1Action" method="{1}"> <result name="t1">/WEB-INF/pages/t1/t1.html</result> <result name="t2">/WEB-INF/pages/t1/t2.html</result> <result name="t3">/WEB-INF/pages/t1/t3.html</result> <result name="toT2" type="redirect" >/t2/t2_t2.do</result> </action> </package> <package name="t2" extends="defalutGlobal" namespace="/t2"> <action name="t2_*" class="test2Action" method="{1}"> <result name="t1">/WEB-INF/pages/t2/t1.html</result> <result name="t2">/WEB-INF/pages/t2/t2.html</result> <result name="t3">/WEB-INF/pages/t2/t3.html</result> </action> </package> <package name="t3" extends="defalutGlobal" namespace="/t3"> <action name="t3_*" class="test3Action" method="{1}"> <result name="t1">/WEB-INF/pages/t3/t1.html</result> <result name="t2">/WEB-INF/pages/t3/t2.html</result> <result name="t3">/WEB-INF/pages/t3/t3.html</result> </action> </package> </struts>
这里为了更好地测试shiro的权限角色控制 所以把 t1,t2,t3加了namespace
7 添加html
这里就没啥说的了 给个缩略图吧
8 实现reaml
public class ShiroDbRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; /** * 认证回调函数,登录时调用. */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = userService.getByUserName(token.getUsername()); if (user != null) { return new SimpleAuthenticationInfo(new ShiroUser(user.getUsername(), user.getNickname()), user.getPassword(),getName()); } else { return null; } } /** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用. */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 加载用户的roles List<Role> roles = roleService.getByUserName(shiroUser.username); List<String> stringRoles = new ArrayList<String>(roles.size()); for (Role role : roles) { stringRoles.add(role.getRolename()); } info.addRoles(stringRoles); // 加载用户的permissions List<Permission> permissions = permissionService.getByUserName(shiroUser.username); Set<String> stringPermissions = new HashSet<String>(permissions.size()); for (Permission permission : permissions) { stringPermissions.add(permission.getPermissionname()); } info.setStringPermissions(stringPermissions); return info; } /** * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息. */ public static class ShiroUser implements Serializable { private static final long serialVersionUID = -1373760761780840081L; private String username; private String nickname; public ShiroUser(String username, String nickname) { this.username = username; this.nickname = nickname; } /**------getset略--------*/ } }
9 action
public class LoginAction extends BaseAction{ private String username; private String password; @Autowired UserService userService; /** * 登录页面 */ public String loginPage(){return "loginPage"; } /** * home页面 */ public String home(){ return "home"; } /** * hello页面 */ public String hello(){ System.out.println(SecurityUtils.getSubject().hasRole("cc")); return "hello"; } /** * 登录 */ public String login(){ Map<String,Object> map = new HashMap<String,Object>(); User u=new User(getUsername(),getPassword()); u=userService.check(u); if("0".equals(u.getRes())){ map.put("res", "true"); AuthenticationToken token = new UsernamePasswordToken(username,password);// username和password是从表单提交过来的 Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token); }else{ map.put("res", "false"); } JSONObject json = JSONObject.fromObject(map);//将map对象转换成json类型数据 setJsonResult(json.toString());//给result赋值,传递给页面 return "success"; } /** * 登录页面 */ public String loginPageForm(){ String result="loginPageForm"; return result; } /** * 登录 */ public String loginForm(){ System.out.println("loginForm"); String result="loginPageForm"; User u=new User(getUsername(),getPassword()); u=userService.check(u); if("0".equals(u.getRes())){ AuthenticationToken token = new UsernamePasswordToken(username,password);// username和password是从表单提交过来的 Subject currentUser = SecurityUtils.getSubject(); currentUser.login(token); result="home"; } return result; } /** * 登出 */ public String logout(){ Subject currentUser = SecurityUtils.getSubject();if (currentUser.isAuthenticated()) { currentUser.logout(); // session 会销毁,在SessionListener监听session销毁,清理权限缓存 if (LOG.isDebugEnabled()) { LOG.debug("用户" + username + "退出登录"); } }return "loginPage"; }
/**--------getset略----------*/
}
这里就只上LoginAction了 其他的action 只是实现的跳转没有啥实际操作 就略过了
如果先看其他action 就只能下源码了
这里还要说一下 登录页面写了两个 一个是ajax的一个是form 没有啥特别的 只是为了之后学习shiro remberme功能 打个提前量
10 数据库结果
create table base_user ( id int not null auto_increment, createtime char(20), username char(20), password char(20), nickname char(20), t1 char(100), t2 char(30), primary key (id) );
好 完成 测试下 成功
最后总结下
1 不得不吐槽下 网上的关于shiro的教程虽然不算少 但大都是 springMVC的 关于struts2的还是比较少的
2 文档还是官方的好 放一个中文的shiro参考手册 下载
3 本人研究shiro时间不长 这里只是作为一个入门参考 如果文中有错误的地方 尽情支出 欢迎技术喷子
4 下次打算把shiro的 rememberme等功能研究下 再写一篇
5 本项目 下载