Springmvc整合Apache Shiro 权限控制。Apache Shiro 的是个优点:
(1)、使用简单、学习成本低。
(2)、体积轻量。
(3)、权限配置灵活,可以实现3层权限校验:无权限访问、用户级、方法级。
1、加载jar包。使用maven管理,添加pom.xml文件
<shiro.version>1.2.4</shiro.version>
<!-- Shiro security -->
<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-cas</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- end of Shiro security -->
2、自定义方法,需要添加ShiroRealm.class,重写AuthenticationInfo(获取认证信息) ,AuthorizationInfo(获取授权信息)方法。
每个系统权限设计都不同,所有需要通过重写这两个方法,实现自己系统的获取认证信息、获取授权信息。
这里提供的是我本身系统的方法,仅仅提供思路,请自行修改。
package framework.common.shiro.shiro;
import org.springframework.beans.factory.annotation.Autowired;
import zteict.qinhuangdao.framework.base.utils.MD5;
import zteict.qinhuangdao.framework.common.shiro.service.ShiroService;
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 java.util.List;
/**
* @author: football98
* @createTime: 16-9-28
* @classDescription:shiro 接口
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private ShiroService shiroService;
/***
* 获取认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) at;
// 通过表单接收的用户名
MD5 md5 = new MD5();
String username = token.getUsername();
String password = md5.getMD5ofStr(String.valueOf(token.getPassword()));
if(!shiroService.findLoginname(username)){
throw new UnknownAccountException(); //如果用户名错误
}
if (!shiroService.getUserByLoginname(username,password)) {
throw new IncorrectCredentialsException(); //如果密码错误
}
return new SimpleAuthenticationInfo(username, token.getPassword(), getName());
}
/***
* 获取授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
// 根据自己系统规则的需要编写获取授权信息,这里为了快速入门只获取了用户对应角色的资源url信息
String loginname = (String) pc.fromRealm(getName()).iterator().next();
if (loginname != null) {
List<String> pers = shiroService.getPermissionsByLoginname(loginname);
if (pers != null && !pers.isEmpty()) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (String each : pers) {
// 将权限资源添加到用户信息中
info.addStringPermission(each);
}
return info;
}
}
return null;
}
}
ShiroService
package framework.common.shiro.service;
import java.util.List;
/**
* @author: football98
* @createTime: 16-9-28
* @classDescription:shiro service接口
*/
public interface ShiroService {
/**
* 判断用户名是否存在
* @param loginname 登录名
* @return 是/否
*/
public boolean findLoginname(String loginname);
/**
* 判断登录名、密码是否正确
* @param loginname 登录名
* @param password 密码
* @return 是/否
*/
public boolean getUserByLoginname(String loginname,String password);
/**
* 获取用户权限列表
* @param loginname 登录名
* @return 用户权限列表
*/
public List<String> getPermissionsByLoginname(String loginname);
}
ShiroServiceImpl
package framework.common.shiro.serviceImpl;
import zteict.qinhuangdao.framework.base.serviceImpl.BaseServiceImpl;
import zteict.qinhuangdao.framework.common.shiro.service.ShiroService;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author: football98
* @createTime: 16-9-28
* @classDescription:shiro service类
*/
@Service("shiroService")
public class ShiroServiceImpl extends BaseServiceImpl implements ShiroService {
/**
* 判断用户名是否存在
* @param loginname 登录名
* @return 是/否
*/
public boolean findLoginname(String loginname){
Session session = this.getSession();
Query q = session.createQuery("select count(tloginid) from Tlogin where loginname = :loginname ");
q.setString("loginname",loginname);
String count = q.uniqueResult()+"";
if(count != null && count.equals("1")){
return true;
}else{
return false;
}
}
/**
* 判断登录名、密码是否正确
* @param loginname 登录名
* @param password 密码
* @return 是/否
*/
public boolean getUserByLoginname(String loginname,String password){
Session session = this.getSession();
Query q = session.createQuery("select count(tloginid) from Tlogin where loginname = :loginname and password = :password");
q.setString("loginname",loginname);
q.setString("password",password);
String count = q.uniqueResult()+"";
if(count != null && count.equals("1")){
return true;
}else{
return false;
}
}
/**
* 获取用户权限列表
* @param loginname 登录名
* @return 用户权限列表
*/
public List<String> getPermissionsByLoginname(String loginname) {
//根据登录名获取用户角色id
StringBuffer sql = new StringBuffer();
sql.append(" select t.troleid ");
sql.append(" from t_logintrole t ");
sql.append(" left join t_login a on t.tloginid = a.tloginid ");
sql.append(" where a.loginname = :loginname ");
String troleid = this.getSession().createSQLQuery(sql.toString()).setString("loginname",loginname).uniqueResult()+"";
//根据角色id获取用户权限url
StringBuffer sql2 = new StringBuffer();
sql2.append(" select distinct t.url ");
sql2.append(" from t_permission t ");
sql2.append(" where t.tpermissionid in (select a.tpermissionid from t_roletpermission a where a.troleid = :troleid) ");
List<String> list = this.getSession().createSQLQuery(sql2.toString()).setString("troleid",troleid).list();
return list;
}
}
3、xml配置
applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans 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.0.xsd ">
<!-- 缓存管理 -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<bean id="shiroRealm" class="framework.common.shiro.shiro.ShiroRealm"/>
<!-- Shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroRealm"></property>
<property name="cacheManager" ref="cacheManager"></property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="/pages/login.jsp"></property>
<property name="unauthorizedUrl" value="/"></property>
<!-- 授权配置 -->
<property name="filterChainDefinitions">
<value>
<!--与用户登录有关的权限 start-->
/pages/login.jsp = anon
/css/** = anon
/images/** = anon
/js/** = anon
/stickyImg* = anon
/loginUserController/login.do = anon
<!--/roleController/save.do = authc , perms["roleController/save.do"] -->
<!--角色模块授权 end-->
/** = authc
</value>
</property>
</bean>
</beans>
applicationContext-springmvc.xml
<!-- 开启Shiro注解的Spring配置方式的beans。在lifecycleBeanPostProcessor之后运行 -->
<aop:config proxy-target-class="true"/>
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
4、方法级权限使用,用户登录方法
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return 结果页面
*/
@RequestMapping(value="login.do")
@Log(name="用户登录")
public String login(String username,String password,HttpServletRequest request) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//token.setRememberMe(true);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
if (subject.isAuthenticated()) {
LoginUserVO vo = loginUserService.loginUser(username);
request.getSession().setAttribute("LOGINUSER", vo);
//用户登录成功后,更新session Map,如重复登录,强制之前session过期
String sessionid = SessionListener.userMap.get(username);
if(sessionid != null&&!sessionid.equals("")){
//注销在线用户,如果session id 相同,不销毁。
if(!sessionid.equals(request.getSession().getId())){
SessionListener.sessionMap.get(sessionid).invalidate();
SessionListener.userMap.put(username,request.getSession().getId());
SessionListener.sessionMap.put(request.getSession().getId(),request.getSession());
}
}else{
if(SessionListener.sessionMap.containsKey(request.getSession().getId())){
SessionListener.sessionMap.remove(request.getSession().getId());
for(String key : SessionListener.userMap.keySet()){
if(SessionListener.userMap.get(key).equals(request.getSession().getId())){
SessionListener.userMap.remove(key);
}
}
}
SessionListener.userMap.put(username,request.getSession().getId());
SessionListener.sessionMap.put(request.getSession().getId(),request.getSession());
}
return "index";
}
} catch (UnknownAccountException e) {
request.setAttribute("ERROR", "用户名错误!");
} catch (IncorrectCredentialsException e) {
request.setAttribute("ERROR", "密码错误!");
}
return "login";
}
在使用时,需要使用@RequiresPermissions()标签来完成。当然,spring必须开放 <context:annotation-config />配置
/**
* 查询角色信息
* @param rolename 角色名
* @param page 当前页
* @param rows 每页显示多少条
* @return 角色信息
*/
@RequestMapping(value = "gridform1.do")
@ResponseBody
@RequiresPermissions("roleController/gridform1.do")
public Object gridform1(String rolename, int page, int rows) {
//总数
int sum = roleService.queryCount(rolename);
//分页信息
Page p = new Page();
p.setIntPage(page);
p.setPageInfoCount(rows);
//查询信息
List<Trole> list = roleService.queryList(rolename, p) ;
//返回结果
Map<String, Object> result = new HashMap<String, Object>() ;
result.put("total",sum);
result.put("rows",list) ;
return result;
}