在 shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能
验证用户身份:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见principals和credentials组合就是用户名/密码了。
身份验证的步骤:
1、收集用户身份/凭证,即如用户名/密码;
2、调用Subject.login 进行登录,如果失败将得到相应的AuthenticationException 异常,根
据异常提示用户错误信息;否则登录成功;
3、最后调用Subject.logout进行退出操作。
如上测试的几个问题:
1、用户名/密码硬编码在ini配置文件,以后需要改成如数据库存储,且密码需要加密存储;
2、用户身份Token 可能不仅仅是用户名/密码,也可能还有其他的,如登录时允许用户名/
邮箱/手机号同时登录。
通过shiro 自带的formAuthenticationFilter 拦截器进行登录,登录失败的话
会把错误存到shiroLoginFailure 属性中,在该控制器中获取后来显示相应的错误信息。
需要在spring-config-shiro.xml中配置
<!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
<property name="loginUrl" value="/login"/>
</bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/login = authc
/logout = logout
/authenticated = authc
/** = user,sysUser
</value>
</property>
</bean>
通过自定义的sysUserFilter用于根据当前登录用户身份获取User信息放入request;然后就可以通过request获取User。
需要在
spring-config-shiro. <bean id="sysUserFilter" class="com.routon.sys.web.shiro.filter.SysUserFilter"/>
密解密
Shiro 提供了CredentialsMatcher 的散列实现HashedCredentialsMatcher,它只用于密码验证,且可以提供自己的盐,而不是随机生成盐,且生成密码散列值的算法需要自己写,因为能提供自己的盐。
生成密码散列值
使用MD5算法,“密码+盐(自定义的盐)”的方式生成散列值
String algorithmName = "md5";
String username = "zhang";
String password = "123456";
String salt = 8d78869f470951332959580424d4bf4f;
int hashIterations = 2;
SimpleHash hash = new SimpleHash(algorithmName, password, salt, hashIterations);
写用户模块时,需要在新增用户/重置密码时使用如上算法保存密码,将生成的密码及
salt存入数据库
生成的密码交给realm
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), //用户名
user.getPassword(), //密码 ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
具体参看:spring-config-shiro.xml
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="com.routon.sys.credentials.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<!--散列迭代次数-->
<property name="hashIterations" value="2"/>
<!--表示是否存储散列后的密码为16 进制,默认是base64 -->
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- Realm实现 -->
<bean id="userRealm" class="com.routon.sys.realm.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
<!--<property name="authenticationCachingEnabled" value="true"/>-->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!--<property name="authorizationCachingEnabled" value="true"/>-->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
</bean>
访问控制
在系统中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作
等)。
通过在执行的方法上放置相应的注解完成:
@RequiresPermissions("sys:role:view")
@RequestMapping(method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("roleList", roleService.findAll());
return "role/list";
}
在JSP页面通过相应的标签完成:
<shiro:hasPermission name="sys:role:view">
<!— 有权限—>
</shiro:hasPermission>