适时的总结和做笔记真的很重要啊,不然过一段时间不用就忘了 - -|||
之前做过一个权限管理的功能,用到了shiro,也是现学现用,总结一下在spring中shiro如何配置。
总的来说配置有这几点:
- 在 web.xml 中配置 Shiro 的 Filter
- 在 Spring 的配置文件中配置 Shiro :
配置自定义 Realm:实现自定义认证和授权
配置 Shiro 实体类使用的缓存策略
配置 SecurityManager
配置保证 Shiro 内部 Bean 声明周期都得到执行的 Lifecycle Bean 后置处理器
配置AOP 式方法级权限检查
配置 Shiro Filter
Shiro与Spring整合配置
1.pom依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<!-- shiro缓存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- shiro-quartz-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.2.2</version>
</dependency>
2.spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!--todo 配置自己的realm -->
<bean id="myRealm" class="com.teamone.security.MyRealm">
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>
<!-- 密码匹配规则-->
<bean id="passwordMatcher" class="com.teamone.security.CustomCredentialsMatcher"/>
<!-- 核心securityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- 自定义过滤器-->
<bean id="myFilter" class="com.teamone.common.RoleAuthFilter"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="filters">
<map>
<entry key="myFilter" value-ref="myFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/static/**=anon
/login=anon
/teller/**=roles[manager]
/account/index=authc
/account/**=myFilter
</value>
</property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
3.spring-web.xml 中添加切面代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!--开启切面编程自动代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<mvc:annotation-driven>
</mvc:annotation-driven>
<!--包扫描-->
<context:component-scan base-package="com.teamone.controller">
</context:component-scan>
<!--开启注解扫描-->
<mvc:annotation-driven/>
<!--处理静态资源-->
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp"/>
</bean>
</beans>
4.web.xml中需要添加shiro的权限过滤器
<!--shiro权限过滤器-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<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>
5.对应spring-shrio注入的bean类(Realm,Filter,密码验证器等)
- ##### 自定义Realm -> MyRealm.java,继承AuthorizingRealm
package com.teamone.security;
import com.teamone.entity.Permission;
import com.teamone.entity.Role;
import com.teamone.entity.User;
import com.teamone.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
/**
* Created by kay on 2017/7/29.
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String)principalCollection.getPrimaryPrincipal();
User user = userService.findUserByUserName(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
for(Role role :user.getRoleList()){
authorizationInfo.addRole(role.getRoleName());
for(Permission permission :role.getPermissionList()){
authorizationInfo.addStringPermission(permission.getPermission());
}
}
return authorizationInfo;
}
/**
* 认证 登录
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToke = (UsernamePasswordToken)authenticationToken;
String username = usernamePasswordToke.getUsername();
User user = userService.findUserByUserName(username);
if(user==null){
return null;
}else {
AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
//将用户信息放入session
SecurityUtils.getSubject().getSession().setAttribute("userinfo",user);
List<String> permissionList=new ArrayList<String>();
for(Role role :user.getRoleList()){
for(Permission permission :role.getPermissionList()){
permissionList.add(permission.getPermission());
}
}
SecurityUtils.getSubject().getSession().setAttribute("USER_PERMISSION",permissionList);
return info;
}
}
// 清除缓存,如果需要则添加
public void clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
}
- ###### CustomCredentialsMatcher.java 密码验证规则重写,继承SimpleCredentialsMatcher
package com.teamone.security;
import com.teamone.utils.MD5Utils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
/**
* Created by kay on 2017/7/29.
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
try {
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
String password = String.valueOf(usertoken.getPassword());
//我这里使用的md5加密,自己写的类MD5Utils,里面进行密码加密
Object tokenCredentials = MD5Utils.encryptPassword(password);
Object accountCredentials =getCredentials(info);
return equals(tokenCredentials,accountCredentials);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return false;
}
}
- ##### 自定义权限验证的Filter,继承自AuthorizationFilter
package com.teamone.common;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
/**
* Created by kay on 2017/8/1.
* 权限对比
*/
public class RoleAuthFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object arg2)
throws Exception {
HttpServletRequest request = (HttpServletRequest) req;
//获取请求路径
String path = request.getServletPath();
Subject subject = getSubject(req, resp);
if (null != subject.getPrincipals()) {
//根据session中存放的用户权限,比对路径,如果拥有该权限则放行
List<String> permissionList= (List<String>) request.getSession().getAttribute("USER_PERMISSION");
//todo 权限检查
if (null != permissionList && permissionList.contains(path)) {
return true;
}
}
return false;
}
/**
*主要用于ajax请求的权限验证,返回403状态
* 会话超时或权限校验未通过的,统一返回403,由前端页面弹窗提示
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws IOException {
if (isAjax((HttpServletRequest) request)) {
WebUtils.toHttp(response).sendError(403);
} else {
String unauthorizedUrl = getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(403);
}
}
return false;
}
//判断是否是ajax请求
private boolean isAjax(HttpServletRequest request) {
String header = request.getHeader("x-requested-with");
if (null != header && "XMLHttpRequest".endsWith(header)) {
return true;
}
return false;
}
}