Spring与shiro整合
一、引入依赖包
在pom.xml文件中引入相关jar包:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.4</version>
</dependency>
Core包是shiro的核心包,web包是对core的扩展,提供web应用所需的一些功能,spring包整合shiro和spring所用的工具包。
二、web.xml文件
添加shiro过滤器:
<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>
DelegatingFilterProxy作用是自动到spring容器查找名字为shiroFilter(filter-name)的bean并把所有Filter的操作委托给它。设置"targetFilterLifecycle"为True,则spring来管理Filter的生命周期。
三、shiro配置文件
1、shiro结构
需要配置的内容包括:shiroFilter,securityManager,realm,sessionManager,其他。
2、配置realm
Realm可以定义一个也可以定义多个。
<!-- 定义realm -->
<bean id="myRealm" class="com.moshenglv.db.realm.MyRealm" />
<bean id="mySecondRealm" class="com.moshenglv.db.realm.MySecondRealm" />
Realm 担当Shiro 和应用程序的安全数据之间的“桥梁”, Realm 来连接一些安全数据源,即获取数据的类,本质上是一个dao。
3、配置sessionManager
Shiro的会话管理器,
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
</bean>
属性包括session失效时间,是否删除失效的session,是否扫描session线程,清理超时会话等。
4、配置securityManager
<!--securityManager是shiro的核心,初始化时协调各个模块运行-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="authenticator" ref="authenticator" />
<property name="realms">
<list>
<ref bean="myRealm" />
<ref bean="mySecondRealm" />
</list>
</property>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- realm验证策略 -->
<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<property name="authenticationStrategy">
<!-- 多个realm只要一个验证通过即可 -->
<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy" />
</property>
</bean>
单个realm使用realm,多个realm,使用realms属性
<property name="realm" ref="myRealm"/>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
在代码中获取sucurityManager的方式为:
SecurityUtils.getSecurityManager()
5、过滤器shiroFilter配置
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!—登陆链接 -->
<property name="loginUrl" value="/user/gologin"/>
<property name="successUrl" value="/book/home"/>
<!—没有权限跳转的链接 -->
<property name="unauthorizedUrl" value="/user/unauthorized"/>
<property name="filterChainDefinitions">
<value>
/book/deleteBook=roles[admin] <!-- 需要admin,才能删除 -->
/book/list=authc <!-- 需要登录才能查询 -->
</value>
</property>
</bean>
6、其他配置
<!-- spring管理shiro bean生命周期 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 启动注解权限控制 -->
<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>
比如在接口上注解:@RequiresRoles("admin"),代表需要角色是admin才能访问。启动注解的配置要写在spring-mvc的配置文件中,也即web.xml中使用ContextLoaderListener加载的配置文件。
四、编写realm
@Service public class MyRealm extends AuthorizingRealm { private static final Logger log = Logger.getLogger(MyRealm.class); @Autowired private UserService userService; @Override public String getName() { return "MyRealm"; } /** * 授权方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { final String name = (String) super.getAvailablePrincipal(principals); log.info("curent user name:" + name); // 授权 final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); final Set<String> rolestr = userService.findRoles(name); final Set<String> permissions = userService.findPermissions(name); info.setRoles(rolestr); info.setStringPermissions(permissions); return info; } /** * 认证方法 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) { final UsernamePasswordToken token = (UsernamePasswordToken) authcToken; // 查询数据库 final User user = userService.getByName(token.getUsername()); if (user != null) { // 如果身份认证验证成功,返回一个AuthenticationInfo实现; return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } return null; } }
在这两个方法中调用了userService中的方法来进行查询账号密码以及在授权方法中根据账号查询得到该用户的角色和权限。userService调用相关的dao在数据库中查询。
五、登陆及授权
1、登陆
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(User currUser) {
final Subject subject = SecurityUtils.getSubject();
final UsernamePasswordToken token = new UsernamePasswordToken(currUser.getUsername(), currUser.getPassword());
try {
subject.login(token);
} catch (final UnknownAccountException uae) {
log.info("没有该用户: " + token.getPrincipal());
} catch (final IncorrectCredentialsException ice) {
log.info(token.getPrincipal() + " 的密码不正确!");
} catch (final LockedAccountException lae) {
log.info(token.getPrincipal() + " 被锁定,请联系管理员");
} catch (final AuthenticationException ae) {
log.info("身份验证失败");
token.clear();
}
1、权限控制
(1)代码控制
@RequestMapping(value = "deleteBook", method = RequestMethod.DELETE)
public String deleteBook(@RequestParamintid) {
final Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole("admin")) {
return "forbidden";
}
try {
bookService.deleteBook(id);
} catch (final Exception e) {
log.error(" 删除错误..", e);
}
returnnull;
}
调用hasRole(“”)的时候会调用realm中的doGetAuthorizationInfo方法进行授权进行权限判断。还有类似的方法isPermission(“”)等。
(2)注解控制
@RequiresRoles("admin")
@RequestMapping(value = "list")
public ModelAndView listUsers() {
………
}
权限不通过会抛出异常。并且默认并没有去处理或者捕获这些异常。需要配置捕获相应异常,如果不配置异常会抛出到页面。
(3)JSP/GSP权限控制
在页面上根据不同权限显示不同的页面,需要引入标签库:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
比如使用标签控制role为admin的用户才能访问:
<shiro:hasRole name="admin">
<a href=”admin.jsp”>管理员</a>
</shiro:hasRole>
其他标签比如:<shiro:unthenticated>,<shiro:user>等
欢迎关注公众号: