1、首先,我们需要将shior需要使用到的jar包依赖进项目中
<!-- shior版本号 -->
<shiro.version>1.2.1</shiro.version>
<!-- 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-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Shiro相关依赖结束 -->
2、依赖完成之后,我们需要增加配置文件了,首先需要将shior需要用到的可配置变量存放到一个文件中,统一使用,那么我们创建一个shior.properties,代码如下:
#shiro 加密方式
password.algorithmName=md5
#shiro 加密迭代次数
password.hashIterations=2
#shior 密码错误次数限制
password.retryCount=3
3、然后我们使用spring整合shior,创建一个配置文件shior-source.xml,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
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-4.3.xsd" >
<!-- 加载属性配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="1"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>classpath:shior.properties</value>
</list>
</property>
</bean>
<!-- 配置SecurityManager对象(Shiro框架核心,负责调用相关组件实现用户身份认证,授权,缓存,会话管理等功能)-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="Realm" ref="shiroUserRealm"/>
</bean>
<!-- 配置ShiroFilterFactoryBean对象(Shiro中会通过很多过滤器对WEB请求做预处理,这些过滤器的创建底层设计了一个工厂类) -->
<bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 注入SecurityManager对象 -->
<property name="SecurityManager" ref="securityManager"/>
<!-- 配置登录页面 -->
<property name="LoginUrl" value="/login.htm"/>
<!-- 定义过滤规则(哪些资源允许匿名访问,哪些资源必须授权访问)-->
<property name="FilterChainDefinitionMap">
<map>
<!-- 说明:anon表示允许匿名访问, authc表示授权访问-->
<entry key="/css/**" value="anon"/>
<entry key="/db_sql/**" value="anon"/>
<entry key="/images/**" value="anon"/>
<entry key="/js/**" value="anon"/>
<entry key="/META-INF/**" value="anon"/>
<!-- 这里是授权的应用,以xml方式来管理,当然也有使用注解或jsp标签的方式,在授权时我们会讲解-->
<!-- <entry key="/company/deleteCompany.do" value="perms[company:delete]" /> -->
<!-- <entry key="/page/xtgl/userList.jsp" value="perms[xtgl:userList]"> -->
<entry key="/doLogin.htm" value="anon"/>
<entry key="/doLogout.do" value="logout"/>
<entry key="/**" value="authc"/>
</map>
</property>
</bean>
<!-- 配置bean对象的生命周期管理 -->
<bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor">
</bean>
<!-- 配置Bean对象的代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
</bean>
<!-- 配置授权Bean对象 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="SecurityManager" ref="securityManager"/>
</bean>
<!--自定义realm对象,这个我们需要自定义,其中有俩个方法,一个为登录验证,一个为权限验证-->
<bean id="shiroUserRealm" class="com.privates.shior.realm.ShiroUserRealm">
<!-- 密码验证方式 -->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
</bean>
<!-- 凭证匹配器,次方法定义了密码验证的一些规则,包括使用的是什么加密方式,加密迭代次数为几次
RetryLimitHashedCredentialsMatcher这个类也需要我们自定义去实现,这个类的作用就是限制密码验证次数及将用户信息放入缓存-->
<bean id="credentialsMatcher" class="com.privates.shior.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager" />
<property name="hashAlgorithmName" value="${password.algorithmName}"/>
<property name="hashIterations" value="${password.hashIterations}"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- 这是shior自带的缓冲,抄就可以了,没有需要改动的地方,当然也可以使用redis代替 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" >
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
</beans>
4、根据配置文件中的注释,来一一增加我们需要自定义的一些东西,首先我们来增加ShiroUserRealm这个比较核心的类,需要继承AuthorizingRealm,代码如下:
package com.privates.shior.realm;
import com.privates.domain.User;
import com.privates.service.UserService;
import com.privates.utils.PasswordHelper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通过此对象获取用户身份相关信息,用户权限相关信息
* 间接的实现了Realm接口
* @author renyf24249
* @version $ID: ShiroUserRealm.java, v 2019/2/22 14:28 Exp $
* AuthenticatingRealm (提供了认证数据的获取方法)
*/
@Service
public class ShiroUserRealm extends AuthorizingRealm {
@Autowired private UserService userService;
/** 自定义缓存map(缓存用户权限信息) **/
private Map<String,SimpleAuthorizationInfo> authorMap = new ConcurrentHashMap<String, SimpleAuthorizationInfo>();
/**此方法提供认证数据的获取操作*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String userName = upToken.getUsername();
// 根据当前的请求登录用户名查询数据库中的用户信息,并判断是否存在该用户
User user = userService.selectByUserName(userName);
if (user == null) {
throw new AuthenticationException("用户名不存在!");
}
//第一个参数为数据库中的用户名,第二个参数为数据库中的密码,第三个参数为,我们当初在insert用户信息时,密码加密时使用到的盐,作用为限制密码反推
//第四个参数为获取我们自定义的这个类的名称 这里的意思就是将我们添加用户成功之后的用户名传给shior,shior根据当前记录的登录信息和我们的数据库中的用户登录信息作比对,验证登录
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUserName(),
user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), super.getName());
// TODO 加入redis缓存
return info;
}
/** 此方法提供权限认证的操作,后文中会讲到 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
5、此时,我们的用户实体类中需要包含盐,每个人的盐是不一样的,所以我们需要记录下他的盐在数据库,用户类代码如下:
package com.privates.domain;
public class User {
/** pk */
private Integer id;
/** 账号 */
private String account;
/** 密码 */
private String password;
/** 简称 */
private String shortName;
/** 真实姓名 */
private String realName;
/** 用户名 */
private String userName;
/** 年龄 */
private String age;
/** 盐(加密时防反推) */
private String salt;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getAccount() { return account; }
public void setAccount(String account) { this.account = account; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getShortName() { return shortName; }
public void setShortName(String shortName) { this.shortName = shortName; }
public String getRealName() { return realName; }
public void setRealName(String realName) { this.realName = realName; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public String getAge() { return age; }
public void setAge(String age) { this.age = age; }
public String getSalt() { return salt; }
public void setSalt(String salt) { this.salt = salt; }
public String getCredentialsSalt() { return this.getSalt(); }
}
6、此时,我们再来写配置文件中缺少的密码适配器,即密码加密规则、次数限制、缓存等信息的管理类RetryLimitHashedCredentialsMatcher,需要继承HashedCredentialsMatcher,代码如下:
package com.privates.shior;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Value;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author renyf24249
* @version $ID: RetryLimitHashedCredentialsMatcher.java, v 2019/2/22 15:09 Exp $
*/
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Value("${password.retryCount}")
private Integer retryCountSystem;
private Cache<String, AtomicInteger> passwordRetryCache;
/** 重试限制哈希凭证匹配器 */
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
/**
* 功能描述: 做证书匹配
* @author renyf24249
* @param token
* @param info
* @return boolean
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
// 判断登录次数
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if (retryCount.incrementAndGet() > retryCountSystem) {
throw new ExcessiveAttemptsException();
}
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
passwordRetryCache.remove(username);
}
return matches;
}
}
注意:这里有个BUG,就是第一次登录错误次数为1,第二次登录错误次数为3,第三次登录次数为5,以此类推,这个需要在真实使用时去解决,我没有解决。
7、配置文件中缺少最后一步就是shior自带缓存的配置了,那么,我们创建一个ehcache.xml文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">
<diskStore path="java.io.tmpdir" />
<!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
eternal="false"
maxElementsInMemory="0"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<cache name="authorizationCache" eternal="false" maxElementsInMemory="0"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="authenticationCache" eternal="false" maxElementsInMemory="0"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro-activeSessionCache" eternal="false" maxElementsInMemory="0"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
注意:如果文件位置放置的不一样,有可能会找不到文件位置,自己注意一下
8、此时,配置文件中的需要自定义的东西我们已经写完,然后需要做的就是,将shior的配置文件加载到web.xml中,并配置shior的filter,作用是拦截所有请求,判断是否授权认证成功,代码如下:
<!-- 指定Spring的配置文件,可以是多个,spring整合每个框架时都可以另写一个spring配置文件 -->
<!-- 否则Spring会默认从WEB-INF下寻找配置文件,contextConfigLocation属性是Spring内部固定的 -->
<!-- 通过ContextLoaderListener的父类ContextLoader的第120行发现CONFIG_LOCATION_PARAM固定为contextConfigLocation -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/conf/spring/spring-context.xml,
/WEB-INF/conf/shior/shior-source.xml
</param-value>
</context-param>
<!-- 配置shiro过滤器(对请求进行拦截) -->
<filter>
<filter-name>shiroFilter</filter-name>
<!-- 此类型由谁提供(spring 框架):spring整合shiro的入口 -->
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 这个参数名在DelegatingFilterProxy中定义 -->
<param-name>targetBeanName</param-name>
<!-- 这个值在spring-shiro.xml配置文件中定义 -->
<param-value>shiroFilterFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
9、到此我们就把所有的shior需要使用的东西都写完了,接下来就是使用shior来做第一个功能了,登录认证,controller代码如下:
package com.skeyedu.controller;
import com.skeyedu.dao.model.Users;
import com.skeyedu.service.UsersService;
import com.skeyedu.util.CommonResult;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Controller
public class LoginController {
@Autowired
private UsersService usersService;
@RequestMapping("/doLogin.htm")
public @ResponseBody CommonResult login(HttpServletRequest request, HttpServletResponse response, Users users) {
CommonResult result = new CommonResult(); result.setIsSuccess(true);
if (users == null || users.getUserAccount() == null) {
result.setResultMsg("请输入用户名!"); return result;
}
try {
//1.获取Subject对象
Subject subject= SecurityUtils.getSubject();
//2.通过Subject提交用户信息,交给shiro框架进行认证操作
//2.1对用户进行封装(身份信息,凭证信息)
UsernamePasswordToken token =
new UsernamePasswordToken(users.getUserAccount(), users.getUserPassword());
//2.2对用户信息进行身份认证
subject.login(token);
result.setIsSuccess(true); result.setResultMsg("登陆成功!");
} catch (UnknownAccountException | IncorrectCredentialsException e) {
result.setResultMsg("用户名/密码错误");
} catch (ExcessiveAttemptsException e) {
// TODO: 此时应该设置用户的状态为锁定
result.setResultMsg("登录失败多次,账户锁定10分钟");
} catch (AuthenticationException e) {
// 其他错误,比如锁定,如果想单独处理请单独catch处理
result.setResultMsg("其他错误:" + e.getMessage());
}
return result;
}
@RequestMapping("/login.htm")
public ModelAndView login() {
ModelAndView view = new ModelAndView("login");
return view;
}
@RequestMapping("/index")
public ModelAndView index(HttpServletRequest request) {
ModelAndView view = new ModelAndView("index");
view.addObject("loginUser", request.getSession().getAttribute("loginUser"));
return view;
}
@RequestMapping("/loginOut")
public @ResponseBody CommonResult loginOut(HttpServletRequest request){
request.getSession().removeAttribute("loginUser");
return new CommonResult(true);
}
}
注意:此时的登录功能只是做了最简单的功能,如有需要自己往上加吧
10、此时就可以尝试去登陆了,当然了,在添加用户的时候记得要将密码加密之后再存入数据库,不然验证是通不过的哟,还要记得保存“盐”到数据库,加密代码如下:
package com.skeyedu.shior;
import com.skeyedu.dao.model.Users;
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;
/**
* @author renyf24249
* @version $ID:PasswordHelper.java, v 2019/2/22 14:56 Exp $
*/
@Service
public class PasswordHelper {
private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
@Value("${password.algorithmName}")
private String algorithmName;
@Value("${password.hashIterations}")
private int hashIterations;
public void encryptPassword(Users user) {
// 判断是否需要生成盐
if (user.getSalt() == null) {
//生成盐(部分,需要存入数据库中)
String random = randomNumberGenerator.nextBytes().toHex();
user.setSalt(random);
}
String newPassword = new SimpleHash(algorithmName, user.getUserPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()), hashIterations).toHex();
user.setUserPassword(newPassword);
}
}
11、然后再添加用户的时候调用一下PasswordHelper的encryptPassword方法就可以了,试一下吧;
注意:其中的密码加密迭代次数需要所有地方都同意,加密方式也是一样,如果不一样就会出现莫名其妙的问题
12、再次之前遇到的问题,第一个就是密码加密方式不正确,导致和shior加密之后的密码进行校验时不匹配,搞了好久,解决办法:一直跟着源码看进去,看到HashedCredentialsMatcher类中写了加密的方法hashProvidedCredentials,源代码如下:
protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
String hashAlgorithmName = this.assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
}
这说明,我们页面上传入12345这个密码时,shior是利用上述方法加密的,那么我们也可以在insert用户时,使用此方式将密码放入数据库,当然了,为了保密性强,还可以再次使用md5加密,匹配校验时先解密再传给shior进行匹配,故此问题解决。
第二个问题就是页面报错,406,一直是在搞json的jar包,springmvc配置json转换,然后增加注解什么的,其实根本的问题在于@RequestMapper中的url的地址后缀问题,我写的是htm,这个后缀springmvc不支持,所以才会解析不了。
13、关于授权问题使用shior,首先需要有权限表,而我们一般系统都是权限对应角色,角色再对应用户,那么我们要做授权,就要拥有这些数据,这里就不一一贴代码了,加入我们拥有一组权限,如下:
用户名:root 角色:人事,前台 权限:用户管理 考勤管理 ,这就是我们的权限信息及角色信息,那么我们需要怎么做呢,首先我们要去实现shior的授权操作,此操作在上文中我们定义的ShiorUserRealm中实现,代码如下:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取当前登录用户
String userName=(String) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
// 定义一个角色集合
Set<String> roles=new HashSet<String>();
// 查询当前用户所拥有的角色列表
List<Map<String, Object>> rolesByUserName = roleDao.getRolesByUserName(userName);
for(Map<String, Object> role : rolesByUserName) {
// 将这些角色放入先前定义的集合中,放入的值是什么,后期写代码时,使用的权限设置的注解中就需要写什么值
// 所以这里建议使用枚举类去处理,方便,又避免写错
roles.add(role.get("roleName").toString());
}
// 查询当前用户所拥有的权限列表
List<Map<String, Object>> permissionsByUserName = permissionDao.getPermissionsByUserName(userName);
for(Map<String, Object> permission:permissionsByUserName) {
// 将权限信息给到shior保存起来,同上所述,这里给shior的字符串就是后期写代码时要使用到的控制权限注解中的参数
// 当某个用户访问某个带有权限注解的controller时,shior就会来这里取该用户的权限,判断是否有将要访问的请求的权限
info.addStringPermission(permission.get("name").toString());
}
// 将角色集合给shior
info.setRoles(roles);
return info;
}
14、此时我们将用户管理的这个controller入口给他加上权限注解,代码如下:
/** 此注解表示,必须拥有用户管理的这个权限才能访问 */
@RequiresPermissions({"用户管理"})
@RequestMapping("/index")
public ModelAndView index() {
return new ModelAndView("user/userList");
}
/** 此注解表示,必须拥有人事这个角色才能访问 */
@RequiresRoles({"人事"})
@RequestMapping(value = "/loadData")
public @ResponseBody DataTables loadData(Users users) {
return new DataTables(usersService.queryByMap(users));
}
此时我们去尝试请求,发现可以请求,但是我们在shior的授权的方法中打了断点,发现并没有进去方法,这说明,我们加的注解没有起作用,那么网上查阅资料,说有俩种办法,第一种,由于我们的shior是通过spring整合的,而spring并没有去管理controller,所以我们在controller中加入权限注解,spring并没有扫描到,自然没起作用,将注解转移到service层就可以了,这个当然不是我们想要的答案,毕竟service中的业务有很多地方在使用,加一个等于控制了多个业务,耦合性太高,第二种是在springmvc的配置文件中加入如下代码:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
而且这段代码还必须加载springmvc的第一个配置文件中,不然也不管用,我们加入进去后,发现,断点进去了,注解起作用了。
15、shior的五种权限注解说明
@RequiresAuthentication : 验证用户是否登录,等同于方法subject.isAuthenticated() 结果为true时。
@RequiresUser : 验证用户是否被记忆,user有两种含义:
一种是成功登录的(subject.isAuthenticated() 结果为true);
另外一种是被记忆的(subject.isRemembered()结果为true)。
@RequiresGuest : 验证是否是一个guest的请求,与@RequiresUser完全相反。
换言之,RequiresUser == !RequiresGuest。 此时subject.getPrincipal() 结果为null.
@RequiresRoles 例如:@RequiresRoles(“aRoleName”);
如果subject中有aRoleName角色才可以访问方法someMethod。如果没有这个权限则会抛出异常AuthorizationException。
@RequiresPermissions 例如: @RequiresPermissions({“file:read”, “write:aFile.txt”} )
要求subject中必须同时含有file:read和write:aFile.txt的权限才能执行方法someMethod()。否则抛出异常AuthorizationException
16、授权实现之后我们发现一个问题,那就是当我们没有某种权限时,我们的系统都是在后台报错,这不符合业务需要啊,我们需要的应该是不显示此菜单,或者是再次点就是提示用户没有此权限,那么我们应该怎么做呢?
查询网上的方法:
需要在springmvc中增加如下配置:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 错误异常转发jsp页面 -->
<prop key="org.apache.shiro.authz.UnauthorizedException">/unauthorized.jsp</prop>
<prop key="org.apache.shiro.authz.UnauthenticatedException">/unauthenticated.jsp</prop>
</props>
</property>
</bean>
或者自定义异常处理类: SimpleMappingExceptionResolver,上述配置中改掉
然后自定义一个异常,继承SimpleMappingExceptionResolver,重写如下方法:
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
try {
// Expose ModelAndView for chosen error view.
BaseResult result = new BaseResult();
if (ex instanceof UnauthorizedException) {
result.setMsg(RespMSG.MSG_UNAUTHORIZED );
result.setStatus(RespMSG.STATUS_UNAUTHORIZED);
} else if (ex instanceof UnauthenticatedException) {
result.setMsg(RespMSG.MSG_UNAUTHENTICATED );
result.setStatus(RespMSG.STATUS_UNAUTHENTICATED);
} else {
result.setMsg(RespMSG.MSG_FAILLED );
result.setStatus(RespMSG.STATUS_FAILLED);
}
response.setHeader("Content-type", "text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(new Gson().toJson(result));
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
看着比较麻烦,没有亲自尝试,有时间再试;
shior分享就到这里;