Apache Shiro的使用

本文将讲述在Spring+SpringMvc项目中使用Shiro来保障系统的安全。关于shiro理论性的东西本文不深入讲解,重点在于在Spring项目中配置和使用shiro.关于理论性的东西,可以看看 张开涛的《跟我学Shiro》。话不多说,先来看看如何配置Shiro。

1 在web.xml中添加一个ShiroFilter


		shiroFilter
		org.springframework.web.filter.DelegatingFilterProxy
		
			targetFilterLifecycle
			true
		
	
	
		shiroFilter
		/*
	

2 在springMvc.xml (SpringWeb上下文配置文件)中配置 支持Shiro对Controller的方法级AOP安全控制

	
	
		
	

	


	
		
			
				
				error/403
				
				
				error/500
			
		
	
	
3 新建一个Spring上下文支持Shiro的配置文件  spring-context-shiro.xml




	Shiro Configuration


	
	
		
	


	
	


	
	
		
		
		
		
			
		
	


	
	
		
			
				/service/login/loginIn = anon		
				/service/admin/** = roles[admin] 	
				/service/** = authc					
			
		
	


	
	
		
	


	
	
		
		
		
	


	
	
		
	


	
	
		
		
		
		
		
		
	


	
	
		
		
	


	
	


	
	
		
		
		
	


4 由于spring-context-shiro.xml中需要一个ehcache-local.xml用于配置ecache缓存配置所以需要创建一个ehcache-local.xml。该xml文件定义了不同的缓存 的配置




	

	
	
	
	
	
	
	
	
	
	
	
	
	
	
    
	
	
	
    
    
	
	
    
    	

5 到此为止,shiro的环境配置我们已经完成了。下面我们在项目中实现shiro

6 由于在4中需要注册一个systemAuthorizingRealm bean .所以创建一个systemAuthorizingRealm类

/**
 * 

Shiro 从此类 获取安全数据(如用户、角色、权限)

* 创建日期: 2017年10月24日 * @author 赵松强 */ public class SystemAuthorizingRealm extends AuthorizingRealm{ @Autowired private IRealmService service; /** 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获取当前已登录的用户 Principal principal = (Principal) getAvailablePrincipal(principals); IUser user = service.getUserByToken(principal.getUserName(),principal.getOfficeId()); if(user == null){ return null; }else{ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//用户权限信息 // 添加用户权限 info.addStringPermission("user"); List list = service.getMenuList(user); for (IMenu menu : list){ if (!"".equals(menu.getPermission())){ // 添加基于Permission的权限信息 for (String permission : menu.getPermission().split(",")){ info.addStringPermission(permission); } } } List rolesList = service.getRoleList(user); // 添加用户角色信息 for (IRole role : rolesList){ info.addRole(role.getRoleEnName()); } return info; } } /** 用户信息认证回调函数, 登录时调用用于验证用户信息 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { //将默认登录口令转换为带officeid的口令 UsernamePasswordToken token =(UsernamePasswordToken) authcToken; //根据用户名和officeid获取用户信息 IUser user = service.getUserByToken(token.getUsername(),token.getOfficeid()); if(user == null){ return null; } if(user.loginEnAble()){//用户可以登录 //返回的信息中包括了一个Principal,项目中可以通过SecurityUtils.getSubject().getPrincipal()获取该信息 return new SimpleAuthenticationInfo(new Principal(user), user.getPassword(), getName()); }else{//如果用户已经被禁止登录 throw new AuthenticationException("msg:该帐号被禁止登录."); } } /** * 用户信息,可以通过 SecurityUtils.getSubject().getPrincipal()获取该信息 */ public static class Principal implements Serializable { private String number;//用户编码(工号、学号等) private String userName;//用户名 private String officeId;//所属组织机构id private String office;//所属组织机构 public Principal(IUser user){ this.number = user.getNumber(); this.userName = user.getUserName(); this.officeId = user.getOfficeId(); this.office = user.getOffice(); } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getOfficeId() { return officeId; } public void setOfficeId(String officeId) { this.officeId = officeId; } public String getOffice() { return office; } public void setOffice(String office) { this.office = office; } } }
7 在6中引用了一个UsernamePasswordToken登陆扣了类,这个类主要是重写shiro的UsernamePasswordToken增加officeid,用于标识用户所属组织机构id。

/**
* 

重写shiro的UsernamePasswordToken增加officeid,用于标识用户所属组织机构id

* 创建日期: 2017年10月26日 * @author 赵松强 */ public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken { private static final long serialVersionUID = 1L; public UsernamePasswordToken(String officeId,String username,String password){ super.setUsername(username); super.setPassword(password.toCharArray()); this.officeid = officeId; } /** * 用户所属组织结构的id */ private String officeid; public String getOfficeid() { return officeid; } public void setOfficeid(String officeid) { this.officeid = officeid; } public static long getSerialversionuid() { return serialVersionUID; } }
8 目前为止,我们已经为shiro提供了数据源,我们可以真正使用它了  这里我新建了一个Shiro工具类,简单写了两个使用的方法

public class ShiroUtils {

	/**
	 * 用户登录时调用登录方法
	 * @param officeId  组织机构id
	 * @param username	用户名
	 * @param password	密码
	 * @return 登录信息
	 * {@link com.swx.cn.rbac.bean.LoginInfo}
	 */
	public static LoginInfo login(String officeId,String username,String password){
		UsernamePasswordToken token = new UsernamePasswordToken(officeId,username,password);
		Subject subject = SecurityUtils.getSubject();
		try {
			subject.login(token);
			return new LoginInfo("登录成功",true);
			
		} catch (AuthenticationException e) {	//登录失败
			
			String className = e.getClass().getName(), message = "";
			if (IncorrectCredentialsException.class.getName().equals(className)
					|| UnknownAccountException.class.getName().equals(className)){//用户名或密码错误导致的异常
				message = "用户名或密码错误, 请重试.";
			}else if (e.getMessage() != null && e.getMessage().contains("msg:")){//非用户名或密码错误导致的异常
				message = e.getMessage().replace("msg:", "");
			}else{//其他异常
				message = "系统出现点问题,请稍后再试!";
				e.printStackTrace(); // 输出到控制台
			}
			return new LoginInfo(message,false);
			
		}
	}

	/**判断用户是否拥有指定的角色标识
	 * @param roleName 角色标识
	 * @return
	 */
	public static boolean hasRole(String roleName){
		Subject subject = SecurityUtils.getSubject();
		return subject.hasRole(roleName);
	}
	
	/**
	 * 获取Shiro的session (Shiro自己实现的session机制,注意不是HttpSession)
	 * @return  Shiro session
	 */
	public static Session getSession(){
		Subject subject = SecurityUtils.getSubject();
		return subject.getSession(true);
	}

}
其实这个类的主要功能都是来自于  SecurityUtils.getSubject() 这个方法返回的 Subject对象。我们可以看看org.apache.shiro.subject.Subject 接口为我们提供了那些使用的方法

public interface Subject {

    Object getPrincipal();

    PrincipalCollection getPrincipals();

    boolean isPermitted(String permission);

    boolean isPermitted(Permission permission);

    boolean[] isPermitted(String... permissions);

    boolean[] isPermitted(List permissions);

    boolean isPermittedAll(String... permissions);

    boolean isPermittedAll(Collection permissions);

    void checkPermission(String permission) throws AuthorizationException;

    void checkPermission(Permission permission) throws AuthorizationException;

    void checkPermissions(String... permissions) throws AuthorizationException;

    void checkPermissions(Collection permissions) throws AuthorizationException;

    boolean hasRole(String roleIdentifier);

    boolean[] hasRoles(List roleIdentifiers);

    boolean hasAllRoles(Collection roleIdentifiers);

    void checkRole(String roleIdentifier) throws AuthorizationException;

    void checkRoles(Collection roleIdentifiers) throws AuthorizationException;

    void checkRoles(String... roleIdentifiers) throws AuthorizationException;

    void login(AuthenticationToken token) throws AuthenticationException;

    boolean isAuthenticated();

    boolean isRemembered();

    Session getSession();

    Session getSession(boolean create);

    void logout();

     V execute(Callable callable) throws ExecutionException;


    void execute(Runnable runnable);


     Callable associateWith(Callable callable);

    Runnable associateWith(Runnable runnable);

    void runAs(PrincipalCollection principals) throws NullPointerException, IllegalStateException;

    boolean isRunAs();

    PrincipalCollection getPreviousPrincipals();

    PrincipalCollection releaseRunAs();

}
使用上面这个接口提供的功能我们基本上能满足所有我们需要shiro 的功能。

9 完成工具类后我们可以在登录方法中调用工具类中的登录方法了

@Controller
@RequestMapping("/login")
public class LoginController {
	
	@RequestMapping("loginIn")
	public String login(HttpServletRequest request, HttpServletResponse response, Model model){
		
		LoginInfo flag = ShiroUtils.login("370000", "郑进", "11111111");
		
		return "login";
	}
}
LoginInfo类里面只是简单封装了下登录的结果
public class LoginInfo {
	/**
	 * 登录信息 主要是承载失败信息
	 */
	private String message;
	
	/**
	 * 登录结果 true:登录成功 false:登录失败
	 */
	private boolean result;
//省略setter getter
}



Spring+SpringMVC环境下使用Shiro已经搭建好了,在系统中可以在控制器的方法上使用

@RequiresPermissions("user:view:123") //标识用户必须要有  user:view:123 才能访问此方法

@RequiresRoles("ceshi") //标识用户必须要有  ceshi 角色才能访问此方法

当然也可以通过 Subject接口提供的方法在任何地方进行 权限验证



你可能感兴趣的:(其他)