</pre><pre name="code" class="html"><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/first" />
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源不需要进行认证 -->
/images/** = anon
/js/** = anon
/styles/** = anon
<!-- 对所有url都需要进行认证 -->
/logout = logout
/** = authc
</value>
</property>
</bean>
首先看一下Shiro中的web filter过滤器:
默认采用的认证过滤器filter是表单过滤器,默认登录的url是/login(只要没有认证的都会跳转到/login路径下),辅助登录成功url是/first。
默认登录url跳转到的页面是login.jsp如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ include file="/WEB-INF/jsp/tag.jsp"%>
<html>
<head>
<TITLE>药品采购平台</TITLE>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/style.css">
<LINK rel="stylesheet" type="text/css" href="${baseurl}styles/login.css">
<LINK rel="stylesheet" type="text/css" href="${baseurl}js/easyui/themes/default/easyui.css">
<LINK rel="stylesheet" type="text/css" href="${baseurl}js/easyui/themes/icon.css">
<STYLE type="text/css">
.btnalink {
cursor: hand;
display: block;
width: 80px;
height: 29px;
float: left;
margin: 12px 28px 12px auto;
line-height: 30px;
background: url('${baseurl}images/login/btnbg.jpg') no-repeat;
font-size: 14px;
color: #fff;
font-weight: bold;
text-decoration: none;
}
</STYLE>
<%@ include file="/WEB-INF/jsp/common_js.jsp"%>
<script type="text/javascript">
//登录提示方法
function loginsubmit() {
$("#loginform").submit();
}
</SCRIPT>
</HEAD>
<BODY style="background: #f6fdff url(${baseurl}images/login/bg1.jpg) repeat-x;">
<FORM id="loginform" name="loginform" action=""
method="post">
<DIV class="logincon">
<DIV class="title">
<IMG alt="" src="${baseurl}images/login/logo.png">
</DIV>
<DIV class="cen_con">
<IMG alt="" src="${baseurl}images/login/bg2.png">
</DIV>
<DIV class="tab_con">
<input type="password" style="display:none;" />
<TABLE class="tab" border="0" cellSpacing="6" cellPadding="8">
<TBODY>
<TR>
<TD>用户名:</TD>
<TD colSpan="2"><input type="text" id="usercode"
name="username" style="WIDTH: 130px" /></TD>
</TR>
<TR>
<TD>密 码:</TD>
<TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
</TD>
</TR>
<%-- <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> --%>
<TR>
<TD colSpan="2" align="center"><input type="button"
class="btnalink" οnclick="loginsubmit()" value="登 录" />
<input type="reset" class="btnalink" value="重 置" /></TD>
</TR>
</TBODY>
</TABLE>
</DIV>
</DIV>
</FORM>
</BODY>
</HTML>
form过滤器有个特点就是,只要是表单提交(条件:1.post 2.action路径为"")就相当于:
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);
他会自动到Real中的方法进行身份认证:
/**
* 身份认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String) token.getPrincipal();
User user = userService.findByUsername(userName);
if(user == null) {
//抛出用户不存在异常
throw new UnknownAccountException();//没找到帐号
}
if(user.getLocked()) {
//抛出用户被锁定异常
throw new LockedAccountException(); //帐号锁定
}
// 如果查询到返回认证信息AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, user.getPassword(),ByteSource.Util.bytes(user.getCredentialsSalt()),
this.getName());
return simpleAuthenticationInfo;
}
值得注意的是SimpleAuthenticationInfo这个方法的构造函数,因为它决定了凭证认证的方式:
1.
public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName) {
this.principals = new SimplePrincipalCollection(principal, realmName);
this.credentials = credentials;
}
该构造器对应的默认任凭类,什么都不需要输入,没有加密算法,没有迭代次数,直接通过用户名和密码进行进行验证就可以。
<bean id="userRealm" class="com.lgy.web.shiro.UserRealm">
<!-- 设置认证凭证器 -->
<!--<property name="credentialsMatcher" ref="credentialsMatcher" /> -->
</bean>
2.
public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
this.principals = new SimplePrincipalCollection(principal, realmName);
this.credentials = hashedCredentials;
this.credentialsSalt = credentialsSalt;
}
这个和你加密的密码salt有关:
package com.lgy.service;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.lgy.model.User;
@Service
public class PasswordHelper {
private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
@Value("${password.algorithmName}")
private String algorithmName;
@Value("${password.hashIterations}")
private int hashIterations;
public void encryptPassword(User user) {
user.setSalt(randomNumberGenerator.nextBytes().toHex());
String newPassword = new SimpleHash(
algorithmName, //加密算法
user.getPassword(), //密码
ByteSource.Util.bytes(user.getCredentialsSalt()), //salt盐 username + salt
hashIterations //迭代次数
).toHex();
user.setPassword(newPassword);
}
}
所以需要设置凭证信息:
<!-- Realm实现 -->
<bean id="userRealm" class="com.lgy.web.shiro.UserRealm">
<!-- 设置认证凭证器 -->
<property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
<!-- 认证凭证器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 算法名称 -->
<property name="hashAlgorithmName" value="${password.algorithmName}" />
<!-- 迭代次数 -->
<property name="hashIterations" value="${password.hashIterations}" />
</bean>
若认证通过后,它会跳转到设置的辅助登录成功url是/first。身份认证就到这里结束!
授权过程如下:
shiro授权有三种方式
Shiro 支持三种方式的授权:
1 编程式:通过写if/else 授权代码块完成:
Subject subject =SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
2 注解式:通过在执行的Java方法上放置相应的注解完成:
@RequiresRoles("admin")
public void hello() {
//有权限
}
3.JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
<shiro:hasRolename="admin">
<!— 有权限—>
</shiro:hasRole>
编程试的不用说了,重点说说注解方式和jsp标签方式:
若使用SpringMVC注解试,需要在SpringMVC的配置文件中配置注解启动:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
在控制器中:
@RequiresPermissions("user:create")
@RequestMapping(value = "/create", method = RequestMethod.GET)
public String showCreateForm(Model model) {
//...
return "user/edit";
}
当进入到这个Controller中的时候,会先进入realm中的:
/**
* 授权认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRoles(user.getUsername()));
authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername()));
return authorizationInfo;
}
权限比较可能有如下2个:
@RequiresPermissions("user:create")
@RequiresRoles("admin")
1.基于角色的认证
2.基于权限码的认证
若使用jsp标签进行认证:
条件:需要导入<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
页面中
<shiro:hasPermission name="user:update">
......
</shiro:hasPermission>
<shiro:hasRole name="">
......
</shiro:hasRole>
同上进入该页面中时候,若出现这样的标签,每出现一个都会调用realm中的:
/**
* 授权认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRoles(user.getUsername()));
authorizationInfo.setStringPermissions(userService.findPermissions(user.getUsername()));
return authorizationInfo;
}
相当于他们调用了shiro中的:
Subject subject = SecurityUtils.getSubject();
subject.checkRole("");
subject.checkPermission("");
*
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:principalproperty="username"/> 显示用户身份中的属性值
当然每次这么做可能浪费的性能很不好,需要配置缓存。