1 shiro与项目集成开发
1.1 shiro与spring web项目整合
shiro与springweb项目整合在“基于url拦截实现的工程”基础上整合,基于url拦截实现的工程的技术架构是springmvc+mybatis,整合注意两点:
1、shiro与spring整合
2、加入shiro对web应用的支持
1.1.1 取消原springmvc认证和授权拦截器
去掉springmvc.xml中配置的LoginInterceptor和PermissionInterceptor拦截器。
1.1.2 加入shiro的jar包
1.1.3 web.xml添加shiro Filter
[html] view plain copy print?
-
<!– shiro过虑器,DelegatingFilterProx会从spring容器中找shiroFilter –>
-
-
<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>
-
-
</filter-mapping>
-
-
1.1.4 applicationContext-shiro.xml
[html] view plain copy print?
-
<!– Shiro 的Web过滤器 –>
-
-
<bean id=“shiroFilter” class=“org.apache.shiro.spring.web.ShiroFilterFactoryBean”>
-
-
<property name=“securityManager” ref=“securityManager” />
-
-
<!– 如果没有认证将要跳转的登陆地址,http可访问的url,如果不在表单认证过虑器FormAuthenticationFilter中指定此地址就为身份认证地址 –>
-
-
<property name=“loginUrl” value=“/login.action” />
-
-
<!– 没有权限跳转的地址 –>
-
-
<property name=“unauthorizedUrl” value=“/refuse.jsp” />
-
-
<!– shiro拦截器配置 –>
-
-
<property name=“filters”>
-
-
<map>
-
-
<entry key=“authc” value-ref=“formAuthenticationFilter” />
-
-
</map>
-
-
</property>
-
-
<property name=“filterChainDefinitions”>
-
-
<value>
-
-
<!– 必须通过身份认证方可访问,身份认 证的url必须和过虑器中指定的loginUrl一致 –>
-
-
/loginsubmit.action = authc
-
-
<!– 退出拦截,请求logout.action执行退出操作 –>
-
-
/logout.action = logout
-
-
<!– 无权访问页面 –>
-
-
/refuse.jsp = anon
-
-
<!– roles[XX]表示有XX角色才可访问 –>
-
-
/item/list.action = roles[item],authc
-
-
/js/** anon
-
-
/p_w_picpaths/** anon
-
-
/styles/** anon
-
-
<!– user表示身份认证通过或通过记住我认证通过的可以访问 –>
-
-
/** = user
-
-
<!– /**放在最下边,如果一个url有多个过虑器则多个过虑器中间用逗号分隔,如:/** = user,roles[admin] –>
-
-
-
-
</value>
-
-
</property>
-
-
</bean>
-
-
-
-
-
-
<!– 安全管理器 –>
-
-
<bean id=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
-
<property name=“realm” ref=“userRealm” />
-
-
</bean>
-
-
-
-
<!– 自定义 realm –>
-
-
<bean id=“userRealm” class=“cn.itcast.ssm.realm.CustomRealm1”>
-
-
</bean>
-
-
<!– 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 –>
-
-
<bean id=“formAuthenticationFilter”
-
-
class=“org.apache.shiro.web.filter.authc.FormAuthenticationFilter”>
-
-
<!– 表单中账号的input名称 –>
-
-
<property name=“usernameParam” value=“usercode” />
-
-
<!– 表单中密码的input名称 –>
-
-
<property name=“passwordParam” value=“password” />
-
-
<!– <property name=”rememberMeParam” value=”rememberMe”/> –>
-
-
<!– loginurl:用户登陆地址,此地址是可以http访问的url地址 –>
-
-
<property name=“loginUrl” value=“/loginsubmit.action” />
-
-
</bean>
securityManager:这个属性是必须的。
loginUrl:没有登录认证的用户请求将跳转到此地址,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。
unauthorizedUrl:没有权限默认跳转的页面。
1.1.5 使用shiro注解授权
在springmvc.xml中配置shiro注解支持,可在controller方法中使用shiro注解配置权限:
[html] view plain copy print?
-
<!– 开启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>
修改Controller代码,在方法上添加授权注解,如下:
[java] view plain copy print?
-
// 查询商品列表
-
-
@RequestMapping(“/queryItem”)
-
-
@RequiresPermissions(“item:query”)
-
-
public ModelAndView queryItem() throws Exception {
上边代码@RequiresPermissions(“item:query”)表示必须拥有“item:query”权限方可执行。
其它的方法参考示例添加注解
1.1.6 自定义realm
此realm先不从数据库查询权限数据,当前需要先将shiro整合完成,在上边章节定义的realm基础上修改。
[java] view plain copy print?
-
public class CustomRealm1 extends AuthorizingRealm {
-
-
-
-
@Autowired
-
-
private SysService sysService;
-
-
-
-
@Override
-
-
public String getName() {
-
-
return “customRealm”;
-
-
}
-
-
-
-
// 支持什么类型的token
-
-
@Override
-
-
public boolean supports(AuthenticationToken token) {
-
-
return token instanceof UsernamePasswordToken;
-
-
}
-
-
-
-
// 认证
-
-
@Override
-
-
protected AuthenticationInfo doGetAuthenticationInfo(
-
-
AuthenticationToken token) throws AuthenticationException {
-
-
-
-
// 从token中 获取用户身份信息
-
-
String username = (String) token.getPrincipal();
-
-
// 拿username从数据库中查询
-
-
// ….
-
-
// 如果查询不到则返回null
-
-
if (!username.equals(“zhang”)) {// 这里模拟查询不到
-
-
return null;
-
-
}
-
-
-
-
// 获取从数据库查询出来的用户密码
-
-
String password = “123”;// 这里使用静态数据模拟。。
-
-
// 根据用户id从数据库取出菜单
-
-
//…先用静态数据
-
-
List<SysPermission> menus = new ArrayList<SysPermission>();;
-
-
SysPermission sysPermission_1 = new SysPermission();
-
-
sysPermission_1.setName(“商品管理”);
-
-
sysPermission_1.setUrl(“/item/queryItem.action”);
-
-
SysPermission sysPermission_2 = new SysPermission();
-
-
sysPermission_2.setName(“用户管理”);
-
-
sysPermission_2.setUrl(“/user/query.action”);
-
-
menus.add(sysPermission_1);
-
-
menus.add(sysPermission_2);
-
-
// 构建用户身体份信息
-
-
ActiveUser activeUser = new ActiveUser();
-
-
activeUser.setUserid(username);
-
-
activeUser.setUsername(username);
-
-
activeUser.setUsercode(username);
-
-
activeUser.setMenus(menus);
-
-
-
-
// 返回认证信息由父类AuthenticatingRealm进行认证
-
-
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
-
-
activeUser, password, getName());
-
-
-
-
return simpleAuthenticationInfo;
-
-
}
-
-
-
-
// 授权
-
-
@Override
-
-
protected AuthorizationInfo doGetAuthorizationInfo(
-
-
PrincipalCollection principals) {
-
-
// 获取身份信息
-
-
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
-
-
//用户id
-
-
String userid = activeUser.getUserid();
-
-
// 根据用户id从数据库中查询权限数据
-
-
// ….这里使用静态数据模拟
-
-
List<String> permissions = new ArrayList<String>();
-
-
permissions.add(“item:query”);
-
-
permissions.add(“item:update”);
-
-
-
-
// 将权限信息封闭为AuthorizationInfo
-
-
-
-
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
-
-
for (String permission : permissions) {
-
-
simpleAuthorizationInfo.addStringPermission(permission);
-
-
}
-
-
-
-
return simpleAuthorizationInfo;
-
-
}
-
-
-
-
}
-
-
1.1.7 登录
[java] view plain copy print?
-
//用户登陆页面
-
-
@RequestMapping(“/login”)
-
-
public String login()throws Exception{
-
-
return “login”;
-
-
}
-
-
// 用户登陆提交
-
-
@RequestMapping(“/loginsubmit”)
-
-
public String loginsubmit(Model model, HttpServletRequest request)
-
-
throws Exception {
-
-
-
-
// shiro在认证过程中出现错误后将异常类路径通过request返回
-
-
String exceptionClassName = (String) request
-
-
.getAttribute(“shiroLoginFailure”);
-
-
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
-
-
throw new CustomException(“账号不存在”);
-
-
} else if (IncorrectCredentialsException.class.getName().equals(
-
-
exceptionClassName)) {
-
-
throw new CustomException(“用户名/密码错误”);
-
-
} else{
-
-
throw new Exception();//最终在异常处理器生成未知错误
-
-
}
-
-
}
1.1.8 首页
由于session由shiro管理,需要修改首页的controller方法:
[java] view plain copy print?
-
//系统首页
-
-
@RequestMapping(“/first”)
-
-
public String first(Model model)throws Exception{
-
-
//主体
-
-
Subject subject = SecurityUtils.getSubject();
-
-
//身份
-
-
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
-
-
model.addAttribute(“activeUser”, activeUser);
-
-
return “/first”;
-
-
}
1.1.9 退出
由于使用shiro的sessionManager,不用开发退出功能,使用shiro的logout拦截器即可。
[html] view plain copy print?
-
<!– 退出拦截,请求logout.action执行退出操作 –>
-
-
/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?
-
<!– 凭证匹配器 –>
-
-
<bean id=“credentialsMatcher”
-
-
class=“org.apache.shiro.authc.credential.HashedCredentialsMatcher”>
-
-
<property name=“hashAlgorithmName” value=“md5” />
-
-
<property name=“hashIterations” value=“1” />
-
-
</bean>
-
-
-
-
<!– 自定义 realm –>
-
-
<bean id=“userRealm” class=“cn.itcast.ssm.realm.CustomRealm1”>
-
-
<property name=“credentialsMatcher” ref=“credentialsMatcher” />
-
-
</bean>
-
-
1.2.2 realm代码
修改realm代码从数据库中查询用户身份信息和权限信息,将sysService注入realm。
[java] view plain copy print?
-
public class CustomRealm1 extends AuthorizingRealm {
-
-
-
-
@Autowired
-
-
private SysService sysService;
-
-
-
-
@Override
-
-
public String getName() {
-
-
return “customRealm”;
-
-
}
-
-
-
-
// 支持什么类型的token
-
-
@Override
-
-
public boolean supports(AuthenticationToken token) {
-
-
return token instanceof UsernamePasswordToken;
-
-
}
-
-
-
-
@Override
-
-
protected AuthenticationInfo doGetAuthenticationInfo(
-
-
AuthenticationToken token) throws AuthenticationException {
-
-
// 从token中获取用户身份
-
-
String usercode = (String) token.getPrincipal();
-
-
-
-
SysUser sysUser = null;
-
-
try {
-
-
sysUser = sysService.findSysuserByUsercode(usercode);
-
-
} catch (Exception e) {
-
-
// TODO Auto-generated catch block
-
-
e.printStackTrace();
-
-
}
-
-
-
-
// 如果账号不存在
-
-
if (sysUser == null) {
-
-
throw new UnknownAccountException(“账号找不到”);
-
-
}
-
-
-
-
// 根据用户id取出菜单
-
-
List<SysPermission> menus = null;
-
-
try {
-
-
menus = sysService.findMenuList(sysUser.getId());
-
-
} catch (Exception e) {
-
-
// TODO Auto-generated catch block
-
-
e.printStackTrace();
-
-
}
-
-
// 用户密码
-
-
String password = sysUser.getPassword();
-
-
//盐
-
-
String salt = sysUser.getSalt();
-
-
// 构建用户身体份信息
-
-
ActiveUser activeUser = new ActiveUser();
-
-
activeUser.setUserid(sysUser.getId());
-
-
activeUser.setUsername(sysUser.getUsername());
-
-
activeUser.setUsercode(sysUser.getUsercode());
-
-
activeUser.setMenus(menus);
-
-
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
-
-
activeUser, password, ByteSource.Util.bytes(salt),getName());
-
-
return simpleAuthenticationInfo;
-
-
}
-
-
-
-
@Override
-
-
protected AuthorizationInfo doGetAuthorizationInfo(
-
-
PrincipalCollection principals) {
-
-
//身份信息
-
-
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
-
-
//用户id
-
-
String userid = activeUser.getUserid();
-
-
//获取用户权限
-
-
List<SysPermission> permissions = null;
-
-
try {
-
-
permissions = sysService.findSysPermissionList(userid);
-
-
} catch (Exception e) {
-
-
// TODO Auto-generated catch block
-
-
e.printStackTrace();
-
-
}
-
-
//构建shiro授权信息
-
-
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
-
-
for(SysPermission sysPermission:permissions){
-
-
simpleAuthorizationInfo.addStringPermission(sysPermission.getPercode());
-
-
}
-
-
return simpleAuthorizationInfo;
-
-
}
-
-
-
-
}
1.3 缓存
shiro每个授权都会通过realm获取权限信息,为了提高访问速度需要添加缓存,第一次从realm中读取权限数据,之后不再读取,这里Shiro和Ehcache整合。
1.3.1 添加Ehcache的jar包
1.3.2 配置
在applicationContext-shiro.xml中配置缓存管理器。
[html] view plain copy print?
-
<!– 安全管理器 –>
-
-
<bean id=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
-
<property name=“realm” ref=“userRealm” />
-
-
<property name=“sessionManager” ref=“sessionManager” />
-
-
<property name=“cacheManager” ref=“cacheManager”/>
-
-
</bean>
-
-
-
-
<!– 缓存管理器 –>
-
-
<bean id=“cacheManager” class=“org.apache.shiro.cache.ehcache.EhCacheManager”>
-
-
</bean>
1.4 session管理
在applicationContext-shiro.xml中配置sessionManager:
[html] view plain copy print?
-
<!– 安全管理器 –>
-
-
<bean id=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
-
<property name=“realm” ref=“userRealm” />
-
-
<property name=“sessionManager” ref=“sessionManager” />
-
-
</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>
1.5 验证码
1.5.1 自定义FormAuthenticationFilter
需要在验证账号和名称之前校验验证码。
[java] view plain copy print?
-
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
-
-
protected boolean onAccessDenied(ServletRequest request,
-
-
ServletResponse response, Object mappedValue) throws Exception {
-
-
-
-
// 校验验证码
-
-
// 从session获取正确的验证码
-
-
HttpSession session = ((HttpServletRequest)request).getSession();
-
-
//页面输入的验证码
-
-
String randomcode = request.getParameter(“randomcode”);
-
-
//从session中取出验证码
-
-
String validateCode = (String) session.getAttribute(“validateCode”);
-
-
if (!randomcode.equals(validateCode)) {
-
-
// randomCodeError表示验证码错误
-
-
request.setAttribute(“shiroLoginFailure”, “randomCodeError”);
-
-
//拒绝访问,不再校验账号和密码
-
-
return true;
-
-
}
-
-
return super.onAccessDenied(request, response, mappedValue);
-
-
}
-
-
}
1.5.2 修改FormAuthenticationFilter配置
修改applicationContext-shiro.xml中对FormAuthenticationFilter的配置。
[html] view plain copy print?
-
<bean id=“formAuthenticationFilter”
-
-
class=“org.apache.shiro.web.filter.authc.FormAuthenticationFilter”>
-
-
-
-
改为
-
-
<bean id=“formAuthenticationFilter”
-
-
class=“cn.itcast.ssm.shiro.MyFormAuthenticationFilter”>
-
-
1.5.3 登陆页面
添加验证码:
[html] view plain copy print?
-
<TR>
-
-
<TD>验证码:</TD>
-
-
<TD><input id=“randomcode” name=“randomcode” size=“8” /> <img
-
-
id=“randomcode_img” src=“${baseurl}validatecode.jsp” alt=“”
-
-
width=“56” height=“20” align=‘absMiddle’ /> <a
-
-
href=javascript:randomcode_refresh()>刷新</a></TD>
-
-
</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?
-
<!– 安全管理器 –>
-
-
<bean id=“securityManager” class=“org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
-
-
<property name=“realm” ref=“userRealm” />
-
-
<property name=“sessionManager” ref=“sessionManager” />
-
-
<property name=“cacheManager” ref=“cacheManager”/>
-
-
<!– 记住我 –>
-
-
<property name=“rememberMeManager” ref=“rememberMeManager”/>
-
-
</bean>
-
-
-
-
<!– rememberMeManager管理器 –>
-
-
<bean id=“rememberMeManager” class=“org.apache.shiro.web.mgt.CookieRememberMeManager”>
-
-
<property name=“cookie” ref=“rememberMeCookie” />
-
-
</bean>
-
-
<!– 记住我cookie –>
-
-
<bean id=“rememberMeCookie” class=“org.apache.shiro.web.servlet.SimpleCookie”>
-
-
<constructor-arg value=“rememberMe” />
-
-
<!– 记住我cookie生效时间30天 –>
-
-
<property name=“maxAge” value=“2592000” />
-
-
</bean>
-
-
修改formAuthenticationFitler添加页面中“记住我checkbox”的input名称:
[html] view plain copy print?
-
<bean id=“formAuthenticationFilter”
-
-
class=“cn.itcast.ssm.shiro.MyFormAuthenticationFilter”>
-
-
<!– 表单中账号的input名称 –>
-
-
<property name=“usernameParam” value=“usercode” />
-
-
<!– 表单中密码的input名称 –>
-
-
<property name=“passwordParam” value=“password” />
-
-
<property name=“rememberMeParam” value=“rememberMe”/>
-
-
<!– loginurl:用户登陆地址,此地址是可以http访问的url地址 –>
-
-
<property name=“loginUrl” value=“/loginsubmit.action” />
-
-
</bean>
1.6.3 登陆页面
在login.jsp中添加“记住我”checkbox。
[html] view plain copy print?
-
<TR>
-
-
<TD></TD>
-
-
<TD>
-
-
<input type=“checkbox” name=“rememberMe” />自动登陆
-
-
</TD>
-
-
</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] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
注:
anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器
2.2 shiro的jsp标签
Jsp页面添加:
<%@ tagliburi=”http://shiro.apache.org/tags” prefix=”shiro” %>
标签名称 |
标签条件(均是显示标签内容) |
<shiro:authenticated> |
登录之后 |
<shiro:notAuthenticated> |
不在登录状态时 |
<shiro:guest> |
用户在没有RememberMe时 |
<shiro:user> |
用户在RememberMe时 |
<shiro:hasAnyRoles name=”abc,123″ > |
在有abc或者123角色时 |
<shiro:hasRole name=”abc”> |
拥有角色abc |
<shiro:lacksRole name=”abc”> |
没有角色abc |
<shiro:hasPermission name=”abc”> |
拥有权限资源abc |
<shiro:lacksPermission name=”abc”> |
没有abc权限资源 |
<shiro:principal> |
显示用户身份名称 |
<shiro:principal property=”username”/> 显示用户身份中的属性值