先在pom文件里导入shiro的jar,这个例子shiro的版本用的是<shiro.version>1.2.2</shiro.version> ,使用@RequiresPermissions等注解,一定要加spring aop的包和org.aspectj.aspectjweaver的jar。
<!-- 导入shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 导入shiro-spring支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 导入shiro-web支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 导入shiro-EHCache支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
然后创建一个sprin-shiro的文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!--定义cookie的属性 -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe" />
<property name="httpOnly" value="true" />
<!-- 默认记住7天(单位:秒) -->
<property name="maxAge" value="604800" />
</bean>
<!-- rememberMe管理器 (org.apache.shiro.codec.Base64).decode('')里面的key可以随意更改
生成方法 在AbstractRememberMeManager类里:
AesCipherService aes = new AesCipherService();
byte[] key = aes.generateNewKey().getEncoded();
String base64 = Base64.encodeToString(key);
-->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('MPdCMZ9urzEA50JDlDYYDg==')}" />
<property name="cookie" ref="rememberMeCookie" />
</bean>
<!--自定义的拦截器 -->
<bean id="remeberFilter" class="com.glodio.shiro.RemeberFilter" />
<!-- shiro配置 用于验证信息和权限 -->
<bean id="realm" class="com.glodio.shiro.ShiroDBRealm" >
<!--密码匹配 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--调用什么加密方法 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!--调用什么加密几次 -->
<property name="hashIterations" value="1"></property>
</bean>
</property>
</bean>
<!--配置 securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm" />
<property name="rememberMeManager" ref="rememberMeManager" />
</bean>
<!--shiroFilter这个名字 一定要和web.xml的名字一样 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!--登录的url 如果没有登录 就会跳转到该页面 -->
<property name="loginUrl" value="/"/>
<!--没权限的url -->
<property name="unauthorizedUrl" value="/"/>
<!--配置自己的过滤器 -->
<property name="filters">
<util:map>
<entry key="remeberFilter" value-ref="remeberFilter"></entry>
</util:map>
</property>
<!--验证规则 只有要一个符合 下面的就不会执行 所以匿名访问的要写在前面 -->
<property name="filterChainDefinitions">
<value>
/ = anon
/login.html = anon
/system/login = anon
/logout = anon
/src/** = anon
/public/** = anon
/** = remeberFilter
</value>
</property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
如果要加 @RequiresPermissions 等注解 ,必须在spring mvc的注解里添加:
<aop:config proxy-target-class="true"/>
<!--在controller层加注解:使用shiro时必须添加的配置-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
在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>
RemeberFilter类:
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.beans.factory.annotation.Autowired;
import com.glodio.bean.Role;
import com.glodio.bean.Url;
import com.glodio.bean.User;
import com.glodio.bean.UserInf;
import com.glodio.service.IRoleService;
import com.glodio.service.IUserService;
import com.glodio.service.UrlService;
import com.glodio.util.StringUtil;
public class RemeberFilter extends AccessControlFilter{
@Autowired
private UrlService urlService;
@Resource
private IRoleService iRoleService;
@Resource
IUserService iUserService;
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
boolean result = false;
Subject subject = SecurityUtils.getSubject();
//这个session 就是httpSession 在web容器下 shiro的session 就是httpSession
Session session = subject.getSession();
//如果登录直接返回true
if(subject.isAuthenticated()){
return true;
}
//如果是记住 没有登录 那么需要初始化session中的用户信息
if(subject.isRemembered() && !subject.isAuthenticated()){
result = true;
User user = (User)session.getAttribute("user");
if(user == null){
String userName = (String) subject.getPrincipal();
user = iUserService.queryBean(userName);
if(user != null){
session.setAttribute("user", user);
initRoles(user,session);
}
}
}
return result;
}
//如果被拒绝 那么直接返回到登录页
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
redirectToLogin(request, response);
return false;
}
private void initRoles(User bean, Session session) {
if (StringUtil.strIsNullOrEmpty(bean.getRolename())) {
return;
}
Role role = iRoleService.selectByRoleName(bean.getRolename());
if (role == null || StringUtil.strIsNullOrEmpty(role.getUrlremark())) {
return;
}
String[] ulrIds = role.getUrlremark().split(";");
if (ulrIds == null || ulrIds.length == 0) {
return;
}
List<Integer> ids = new ArrayList<>();
for (String id : ulrIds) {
ids.add(Integer.parseInt(id));
}
List<String> permissions = new ArrayList<>();
List<Url> urls = urlService.selectListByIds(ids);
if (urls != null) {
session.setAttribute("urls", urls);
for(Url url : urls){
permissions.add(url.getUrlAddr());
}
}
UserInf userInf = new UserInf();
userInf.setUsr(bean);
userInf.setPermissions(permissions);
session.setAttribute(UserInf.USERINF, userInf);
}
}
ShiroDBRealm类:
import javax.annotation.Resource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import com.glodio.bean.User;
import com.glodio.bean.UserInf;
import com.glodio.service.IUserService;
public class ShiroDBRealm extends AuthorizingRealm {
@Resource
private IUserService iUserService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
Session session = SecurityUtils.getSubject().getSession();
UserInf userInf = (UserInf) session.getAttribute(UserInf.USERINF);
SimpleAuthorizationInfo authorizationInfo = null;
if(userInf != null){
authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addStringPermissions(userInf.getPermissions());
}
return authorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
// 把token转换成User对象
UsernamePasswordToken upt = (UsernamePasswordToken)authcToken;
String username = upt.getUsername();
String password = String.valueOf(upt.getPassword());
User userLogin = tokenToUser(upt);
// 根据验证用户名查找用户 这里可以根据用户状态 抛出多个异常 然后在login方法里进行捕捉
User u = iUserService.queryBeanPassword(username);
if(u == null) {
throw new UnknownAccountException("用户不存在");
}
//当前 Realm 的 name
String realmName = this.getName();
//principal 就是用户名
Object principal = upt.getPrincipal();
ByteSource salt = ByteSource.Util.bytes(username);
//会验证密码是否匹配
return new SimpleAuthenticationInfo(principal, u.getPassword(), salt,realmName);
}
private User tokenToUser(UsernamePasswordToken authcToken) {
User user = new User();
user.setUsername(authcToken.getUsername());
user.setPassword(String.valueOf(authcToken.getPassword()));
return user;
}
}
login方法:
@ResponseBody
@RequestMapping("/login")
public Map<String, Object> login(HttpServletRequest request, @Param("username") String username,
@Param("password") String password) throws Exception {
Map<String, Object> dataMap = new HashMap<>();
boolean result = false;
User bean = iUserService.queryBeanPassword(username);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
//捕捉登录出现的异常
try {
//是否记住用户名,shiro 在cookie里面只存了用户名
usernamePasswordToken.setRememberMe(true);
subject.login(usernamePasswordToken);
if(subject.isAuthenticated()){
result = true;
}
// if no exception, that's it, we're done!
} catch (UnknownAccountException uae) {
// username wasn't in the system, show them an error message?
result = false;
dataMap.put("msg", "当前用户名不存在!");
} catch (IncorrectCredentialsException ice) {
dataMap.put("msg", "用户名或密码错误!");
} catch (LockedAccountException lae) {
// account for that username is locked - can't login. Show them a
// message?
} catch (AuthenticationException ae) {
ae.printStackTrace();
dataMap.put("msg", "系统出错,请稍后重试!");
}
if (result) {
if (bean != null ) {
result = true;
request.getSession().setAttribute("user", bean);
// String rolename = bean.getRolename();
initRoles(bean, request.getSession());
}
}
dataMap.put("result", result);
return dataMap;
}
权限验证:在controller 里面的方法 加@RequiresPermissions等注解就行。如果没有这个权限,会抛出异常 ,所有必须捕获这个异常
@RequiresPermissions("src/org.html")
@RequestMapping("/OrgList")
public @ResponseBody Map<String, Object> OrgList(HttpServletRequest request) throws Exception{
Map<String, Object> dataMap = new HashMap<>();
Map<String, Object> map = new HashMap<String, Object>();
ArrayList<Org> list = null;
int total = 0;
int pageNumber = Integer.parseInt(request.getParameter("pageNumber"));
int pageSize = Integer.parseInt(request.getParameter("pageSize"));
if(pageNumber > 0) {
pageNumber = (pageNumber-1)*pageSize;
}
else {
pageNumber = 0;
}
map.put("pageNumber", pageNumber);
map.put("pageSize", pageSize);
// 获取当前用户bean,取得用户所在机构,根据所在机构获取用户所有机构权限,再根据所有机构的显示数据信息
User user = (User)request.getSession().getAttribute("user");
List<Integer> orgIdList = iOrgService.getOrgIdCollection(user.getOrgId());
map.put("list", orgIdList);
total = iOrgService.queryAllCountForOrgId(orgIdList);
if(total > 0) {
list = iOrgService.queryBeansForOrgId(map);
}
total = (total+pageSize)/pageSize;
dataMap.put("total", total);
dataMap.put("list", list);
return dataMap;
}
CustomExceptionResolver类:简单的捕获了AuthorizationException异常
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ex.printStackTrace();
//对异常类型进行判断
if(ex instanceof AuthorizationException){
response.setCharacterEncoding("utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
writer.println("没有该权限!");
writer.close();
return null;
}else{
return new ModelAndView();
}
}
}
Filter Name Class
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
这个可以看出每个过滤器名称都有一个过滤器类,所以
<property name="filterChainDefinitions">
<value>
/ = anon
/login.html = anon
/system/login = anon
/logout = anon
/src/** = anon
/public/** = anon
/** = remeberFilter
</value>
</property>
我们自己可以定义一些过滤器来控制访问的权限 或 做一些其他事