SSM集成Shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。 —— [ 百度百科 ]
第一次写博客,很多地方可能语句不通,只是作为项目当中的一些记录.开发工具使用的Eclipse.项目刚开始代码还在逐步完善中,请多多指教.jsp使用了JQuery,Easyui前段框架[Github][2].
贴上pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jxj</groupId>
<artifactId>ssm-shiro-0624-jxj</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>ssm-shiro-0624-jxj Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- 统一版本号 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- spring版本号 -->
<spring.version>3.2.4.RELEASE</spring.version>
<!-- mybatis版本号 -->
<mybatis.version>3.2.4</mybatis.version>
<!-- log4j日志文件管理包版本 -->
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.9</log4j.version>
<shiro.version>1.3.2</shiro.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<!-- scope=compile 的情况(默认compile),也就是说这个项目在编译、测试,运行阶段都需要这个artifact对应的jar包在classpath中 -->
<!-- scope=provided ,则可以认为这个provided是目标容器已经provided这个artifact,它只影响到编译、测试阶段,运行阶段,假定目标容器已经提供了这个jar包 -->
<scope>provided</scope>
</dependency>
<!-- spring 核心包 -->
<!-- springframe start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- 数据源包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.2</version>
</dependency>
<!-- json数据 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.ezmorph/ezmorph -->
<dependency>
<groupId>net.sf.ezmorph</groupId>
<artifactId>ezmorph</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
<!-- 日志文件管理包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 格式化对象,方便输出日志 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.32</version>
<scope>provided</scope>
</dependency>
<!-- 映入JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- 上传组件包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8</version>
<scope>test</scope>
</dependency>
<!--jsp标签 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Shiro-->
<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-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
</dependencies>
<!-- 定制打包后的包名 -->
<build>
<finalName>0619-jxj</finalName>
</build>
</project>
一.先建立一个maven工程,集成Spring和MyBatis(略过)
二.使用MyBatis的代码生成dao及对应的xml和实体类
生成过程:(放在其他文件夹中)
1.新建一个文件夹jxj(名字,位置自定)
2.将mybatis-3.4.1 和mybatis-generator-core-1.3.5 和mysql-connector-java-5.1.38 的jar包放到文件夹中
3.再新建一个xml文件.代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动-->
<classPathEntry location="mysql-connector-java-5.1.38.jar"/>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库链接URL,用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/zjtblog" userId="root" password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- 生成模型的包名和位置-->
<javaModelGenerator targetPackage="test.domain" targetProject="src">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成映射文件的包名和位置-->
<sqlMapGenerator targetPackage="test.mapping" targetProject="src">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成DAO的包名和位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="test.Dao" targetProject="src">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
<table tableName="t_role_menu" domainObjectName="Tuser" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
4.再新建一个文件夹用户存放生成的代码
6.打开cmd定位到这个文件夹,执行:
java -jar mybatis-generator-core-1.3.5.jar -configfile generatorConfig.xml -overwrite
注:由于不是在项目中生成代码,所以xml文件里面的路径要同项目里的路径要一样,这样生成后可以直接复制进去
三 集成shiro
1. spring-mvc.xml
2. spring-mybatis.xml
3. spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 自定义Realm -->
<bean id="userRealm" class="com.jxj.shiro.UserRealm">
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
<!-- 注入缓存管理器
<property name="cacheManager" ref="cacheManager"/> -->
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager" />
<!-- 记住我
<property name="rememberMeManager" ref="rememberMeManager"/> -->
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的失效时长,单位毫秒 -->
<property name="globalSessionTimeout" value="600000"/>
<!-- 删除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
</bean>
<!-- 启用shrio授权注解拦截方式 -->
<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 调用我们配置的权限管理器 -->
<property name="securityManager" ref="securityManager" />
<!-- 配置我们的登录请求地址 -->
<property name="loginUrl" value="/user/login" />
<!-- 配置我们在登录页登录成功后的跳转地址,因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) -->
<!--<property name="successUrl" value="/jsp/index" /> -->
<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
<property name="unauthorizedUrl" value="/user/hello" />
<!-- 权限配置 -->
<property name="filterChainDefinitions">
<value>
<!-- anon表示此地址不需要任何权限即可访问 -->
/user/login=anon
/icon/**=anon
/js/**=anon
/css/**=anon
/images/**=anon
/user/logout=logout
/user/hello=anon
<!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
/** = authc
</value>
</property>
</bean>
<!-- 开启aop,对类代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- 开启shiro注解支持 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- <bean id="accountService" class="com.jxj.service.impl.UserServiceImpl">
<property name="userService" ref="userService"></property>
</bean> -->
</beans>
4.自定义的 UserRealm
4.1 UserRealm 集成 AuthorizingRealm 有两个方法:
doGetAuthorizationInfo 授权
doGetAuthenticationInfo 验证
验证就是将账号和密码交给 SimpleAuthenticationInfo 来验证
System.out.println("登陆验证....");
String username = (String) token.getPrincipal();//传进来的账号
User user = userService.findByUsername(username);//根据账号从数据库查询用户信息
if(user == null) {
System.out.println("======不存在该用户=========>");
throw new UnknownAccountException("不存在该用户");//没找到帐号
}
System.out.println("数据库的账号:"+user.getUserName()+" 密码:"+user.getPassword());
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUserName(), //用户名
user.getPassword(), //密码
getName()
);
return authenticationInfo;
授权这里我花了一些时间,所以多写点:
第一步 获取当前用户
String username = (String)pc.getPrimaryPrincipal();
判断用户不为空后,从数据库获取用户对应对的角色将其放进去
//获取用户角色
List<String> roles = getRoleList(username);
authorizationInfo.addRoles(roles);
/**
* 根据用户 获取部门集
* @param username
* @return
*/
private List<String> getRoleList(String username){
List<String> role = new ArrayList<String>();
List<Role> roles = userService.getRoleByUsername(username);
for(Role r : roles) {
String str = username+":"+r.getName();
role.add(str);
};
return role;
}
根据用户获取角色集后,以 用户名:角色名 的集合,
在控制器中就可以获取是否有这个角色的权限
我的项目是 以资源和资源对应的按钮权限
首先从数据库获取资源 名称和对应的增删改查等权限分别放入集合,然后再嫁给shiro
/**
* 获取 用户对应的资源权限
* @param username
* @return
*/
private List<String> getPermissionList(String username){
List<String> permission = new ArrayList<String>();
List<RoleMenu> menulist =menuService.selectRoleMenuByUserName(username);
for(RoleMenu rm : menulist) {
String menuName = menuService.menuById(rm.getMenuId()).getName(); //菜单名称
//String roleName = roleService.getRoleByRoleId(rm.getRoleId()).getName(); //部门名字
if(rm.getAddTx().equals("checked")) {
permission.add(menuName+":add");
};
if(rm.getDelTx().equals("checked")) {
permission.add(menuName+":del");
};
if(rm.getEditTx().equals("checked")) {
permission.add(menuName+":edit");
};
if(rm.getSeeTx().equals("checked")) {
permission.add(menuName+":see");
};
permission.add(menuName+":power:"+rm.getPowertx());
};
return permission;
}
授权:
/**
* 获取权限信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
System.out.println("UserRealm当前用户授权");
String username = (String)pc.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
if (username!=null) {
//获取用户角色
List<String> roles = getRoleList(username);
authorizationInfo.addRoles(roles);
//资源集按钮
List<String> mlist=getPermissionList(username);
authorizationInfo.addStringPermissions(mlist);
}else {
System.out.println("UserRealm当前用户没有获取到用户");
}
return authorizationInfo;
}
到这里就完成了授权:
在控制器中就可以查看是否有某个权限了,我要修改用户资料:
/**
* 编辑用户
* @param str 接收的用户id
* @param request
* @return 页面地址
*/
@RequestMapping("/edit")
public String editUserInfo( Integer uid,HttpServletRequest request) {
System.out.println("-----------------开始修改用户资料"+uid);
Subject subject = SecurityUtils.getSubject();
boolean isPermitted =subject.isPermitted("用户管理:edit");
User user = userService.selectUser(uid);
if(isPermitted) {
List<Map> roleNames = getRoleName();
request.setAttribute("roleNames",roleNames);
request.setAttribute("user",user);
request.setAttribute("message","edit");
return "user/user-detail";
}else {
request.setAttribute("message","修改用户管理");//
return "Exception/jurisdictionException";
}
};
这样基本完成了登录及授权的工作的
这里没有加入缓存,所以每次都会查询数据库来获取相关信息
展示层: 用的Jquery和Easyui的框架,并没有用到其他的框架.因为时间的关系,除了用到的功能外,其他的都没有处理,所以页面有些不协调也有些简陋.
因为每个元素都是自己加上去的所以自由性大写,可以自己发挥
贴几张图片(确实有点丑)
浏览器兼容
- 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
- IE9以下不支持
- IE9,10,11存在以下问题
- 不支持离线功能
- IE9不支持文件导入导出
- IE10不支持拖拽文件导入