shiro 概念总结

shiro 流程大概总结:

JSP:

配置loginUrl,在此URL返回的视图里,会有个form(此form无须指明action,我想是因为配置了loginUrl的关系,但是配置了authc 为loginUrl),提交后,就会进入FormAuthenticationFilter,你可以重写他,处理自己的逻辑,如写response为JSON的,等等。这样如果登陆成功了,会根据之前访问的URL,和配置的SUCCEEURL作为备选,优先是去之前的URL,(有个疑问,如果之前的是login,那现在登陆成功后还是login?或者处理了下如果是login就返回备选URL,如果没有配successUrl呢,是不是应该有什么必须的东西再里面)。这样就访问到之前的URL了,一般都是/,就是首页了,controller里面定义下这个这个返回方法就行(可以写个mainController,用来处理首页的。有时候还可以把 / 配置成重定向到另外一个controler,不提了)

JSON:

上面都是JSP的,不适合EXT,同样需要配置loginUrl,登陆操作不是用form来了,用ajax提交的,同样配置了authc 为loginUrl,同样进FormAuthenticationFilter,这时候肯定要重新它了,让它返回JSON,自己在JS sucess里面回调重定向到首页(wecome/index/home 这种名字的)。

还有个注意的,所谓的shiro权限字符串(如 "admin:session:forceLogout"),这玩意没什么特别的地方,就是一个ID,一个有意义的ID,我之前一直以为shiro会怎么处理它,发现就是ID而已,甚至可以用 其他什么ID来代替它,这玩意就是用来唯一标识一个权限的。一般可以在controller里面方法上面 加入注解 @RequiresPermissions("admin:session:forceLogout")(也可以在页面上用shiro 标签 包裹某段代码,用以实现该段代码是否显示)。

(上面这段错了,还是有意义的,shiro会用通配符来匹配,两边进行equals,

 @Override  
    public boolean implies(Permission p) {  
        if(!(p instanceof BitPermission)) {  
            return false;  
        }  
        BitPermission other = (BitPermission) p;  
        if(!("*".equals(this.resourceIdentify) || this.resourceIdentify.equals(other.resourceIdentify))) {  
            return false;  
        }  
        if(!(this.permissionBit ==0 || (this.permissionBit & other.permissionBit) != 0)) {  
            return false;  
        }  
        if(!("*".equals(this.instanceId) || this.instanceId.equals(other.instanceId))) {  
            return false;  
        }  
        return true;  
    }  

 ,只有当是通配符,或者相等时候,返回 TRUE),就是分解,然后三个部分进行相等比较,三个部分表示



 

在spring-mvc配置文件里统一处理异常

 <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
         <property name="exceptionMappings">  
             <props>  
                 <prop key="java.lang.Exception">error/500</prop>  
                 <prop key="java.lang.RuntimeException">error/500</prop>  
                 <prop key="java.lang.Throwable">error/500</prop>  
                 <prop key="UnauthorizedException">error/noRealm</prop>  
             </props>  
         </property>    
         <!-- 设置日志输出级别,不定义则默认不输出警告等错误日志信息 -->  
         <property name="warnLogCategory" value="WARN"></property>  
         <!-- 默认错误页面,当找不到上面mappings中指定的异常对应视图时,使用本默认配置 -->  
         <property name="defaultErrorView" value="error/500"></property>  
         <!-- 默认HTTP状态码 -->  
         <property name="defaultStatusCode" value="500"></property>  
     </bean>  

这里可能JSON的就不是这么处理了。(如果权限粒度只到菜单 这个返回页面的 ,不到操作按钮的这种AJAX 返回JSON的,是可以满足的)。

总结,主要还是AuthorizingRealm这个类,和FormAuthenticationFilter这个类

public class ShiroDbRealm extends AuthorizingRealm {
	
	private static Logger logger = LoggerFactory.getLogger(ShiroDbRealm.class);
	
	
	@Autowired
	protected AccountService accountService;

	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		logger.info("doGetAuthenticationInfo----");
		
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		User user = null;
		try {
			user = accountService.findUserByLoginName(token.getUsername());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (user != null) {
			
			byte[] salt = Encodes.decodeHex(user.getSalt());//16进制的
			return new SimpleAuthenticationInfo(user,
					user.getPassword(), ByteSource.Util.bytes(salt), getName());
			
//			return new SimpleAuthenticationInfo();
		} else {
			return null;
		}
	}
	
	/**
	 * 设定Password校验的Hash算法与迭代次数.
	 */
	@PostConstruct
	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(AccountService.HASH_ALGORITHM);
		matcher.setHashIterations(AccountService.HASH_INTERATIONS);
		setCredentialsMatcher(matcher);
		
	}
	
	

	/**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        //Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		try {
			User user = this.userService.getUserByName(username);
	        Set<String> roles = new HashSet<String>();
	        //本系统设计为一个用户属于一个用户组,即用户组就是用户的角色(employee、finance、hr、boss..);每个用户组有不同的权限(资源)
	        //其他系统中可以设置 一个用户有多个角色,一个角色有多个权限
	        //在本系统中 除了管理员是admin其他组都用user标识,除了老板,其他用户组的操作都和员工组一样的。
	        roles.add("admin".equals(user.getGroup().getType())?"admin":"user");
	        
	        List<GroupAndResource> grList = this.grService.getResource(user.getGroup().getId());
	        Set<String> resources = new HashSet<String>();
	        for(GroupAndResource gr : grList){
	        	Resource resource = this.resourceService.getPermissions(gr.getResourceId());
	        	if(!BeanUtils.isBlank(resource)){
	        		resources.add(resource.getPermission());
	        	}
	        }
	        
	        authorizationInfo.setRoles(roles);
	        authorizationInfo.setStringPermissions(resources);
	        
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			logger.error("realm 错误!");
		}
		return authorizationInfo;
    }


	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {  
	private static final Logger log = LoggerFactory
			.getLogger(CustomFormAuthenticationFilter.class);
	
	 /** 
     * 所有请求都会经过的方法。 
     */  
    @Override  
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {  
    	 if (isLoginRequest(request, response)) {  
             if (isLoginSubmission(request, response)) {  
                 if (log.isTraceEnabled()) {  
                     log.trace("Login submission detected.  Attempting to execute login.");  
                 }  
                 //验证码暂不处理
//                 if ("XMLHttpRequest"  
//                         .equalsIgnoreCase(((HttpServletRequest) request)  
//                                 .getHeader("X-Requested-With"))) {// 不是ajax请求  
//                     String vcode = request.getParameter("vcode");  
//                     HttpServletRequest httpservletrequest = (HttpServletRequest) request;  
//                     String vvcode = (String) httpservletrequest  
//                             .getSession()  
//                             .getAttribute("com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY"  
//                                     );  
//                     if (vvcode == null || "".equals(vvcode)  
//                             || !vvcode.equals(vcode)) {  
//                         response.setCharacterEncoding("UTF-8");  
//                         PrintWriter out = response.getWriter();  
//                         out.println("{success:false,msg:'验证码错误'}");  
//                         out.flush();  
//                         out.close();  
//                         return false;  
//                     }  
//                 }  
                 return executeLogin(request, response);  
             } else {  
                 if (log.isTraceEnabled()) {  
                     log.trace("Login page view.");  
                 }  
                 // allow them to see the login page ;)  
                 return true;  
             }  
         } else {  
             if (log.isTraceEnabled()) {  
                 log.trace("Attempting to access a path which requires authentication.  Forwarding to the "  
                         + "Authentication url [" + getLoginUrl() + "]");  
             }  
             if (!"XMLHttpRequest"  
                     .equalsIgnoreCase(((HttpServletRequest) request)  
                             .getHeader("X-Requested-With"))) {// 不是ajax请求  
                 saveRequestAndRedirectToLogin(request, response);  
             } else {  
                 response.setCharacterEncoding("UTF-8");  
                 PrintWriter out = response.getWriter();  
                 out.println("{msg:'login'}");  
                 out.flush();  
                 out.close();  
             }  
             return false;  
         }
    }  
    
    /**
     * 覆盖默认实现,用sendRedirect直接跳出框架,以免造成js框架重复加载js出错。
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception  
     * @see org.apache.shiro.web.filter.authc.FormAuthenticationFilter#onLoginSuccess(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.subject.Subject, javax.servlet.ServletRequest, javax.servlet.ServletResponse)
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
            ServletRequest request, ServletResponse response) throws Exception {
        //issueSuccessRedirect(request, response);
        //we handled the success redirect directly, prevent the chain from continuing:
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;
         
        if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With")) 
                && request.getParameter("ajax") == null) {// 不是ajax请求 让他重定向到页面
        	issueSuccessRedirect(request, response);
        } else {//在ajax里面,用不了shiro配置的successurl重定向功能,只能在回调函数里重定向     wj 2016-9-7
        	httpServletResponse.setCharacterEncoding("UTF-8");
			PrintWriter out = httpServletResponse.getWriter();
			out.println("{success:true,msg:'登入成功'}");
			out.flush();
			out.close();
        }
         
        return false;
    }
    
    
    /** 
     * 主要是处理登入失败的方法 
     */  
    @Override  
    protected boolean onLoginFailure(AuthenticationToken token,  
            AuthenticationException e, ServletRequest request,  
            ServletResponse response) {  
        if (!"XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request)  
                .getHeader("X-Requested-With"))) {// 不是ajax请求  
            setFailureAttribute(request, e);  
            return true;  
        }  
        try {  
            response.setCharacterEncoding("UTF-8");  
            PrintWriter out = response.getWriter();  
            String msg = e.getClass().getSimpleName();  
            if ("IncorrectCredentialsException".equals(msg)) {  
                out.println("{success:false,msg:'密码错误'}");  
            } else if ("UnknownAccountException".equals(msg)) {  
                out.println("{success:false,msg:'账号不存在'}");  
            } else if ("LockedAccountException".equals(msg)) {  
                out.println("{success:false,msg:'账号被锁定'}");  
            } else {  
                out.println("{success:false,msg:'未知错误'}");  
            }  
            out.flush();  
            out.close();  
        } catch (IOException e1) {  
            // TODO Auto-generated catch block  
            e1.printStackTrace();  
        }  
        return false;  
    }  


猜你喜欢

转载自cainiao1923.iteye.com/blog/2337812