All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ars.module.people.service.StandardAuthService Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package ars.module.people.service;

import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.io.IOException;
import java.io.ByteArrayOutputStream;

import javax.imageio.ImageIO;

import ars.util.Cache;
import ars.util.Beans;
import ars.util.Opcodes;
import ars.util.Strings;
import ars.util.SimpleCache;
import ars.server.Servers;
import ars.invoke.Invokes;
import ars.invoke.request.Token;
import ars.invoke.request.Requester;
import ars.invoke.request.Session;
import ars.invoke.request.TokenInvalidException;
import ars.invoke.request.AccessDeniedException;
import ars.invoke.event.InvokeListener;
import ars.invoke.event.InvokeBeforeEvent;
import ars.module.people.model.User;
import ars.module.people.model.Role;
import ars.module.people.model.Logined;
import ars.module.people.assist.Passwords;
import ars.module.people.service.AuthService;
import ars.database.repository.Repository;
import ars.database.repository.Repositories;
import ars.database.service.event.UpdateEvent;
import ars.database.service.event.ServiceListener;

/**
 * 用户认证业务操作标准实现
 * 
 * @author yongqiangwu
 *
 */
public class StandardAuthService
		implements AuthService, InvokeListener, ServiceListener {
	/**
	 * 令牌缓存标识前缀
	 */
	public static final String TOKEN_CACHE_PREFIX = "token_";

	/**
	 * 用户权限标识
	 */
	public static final String TOKEN_KEY_PERMISSION = "permission";

	/**
	 * 登录失败次数会话标识
	 */
	public static final String SESSION_KEY_ERRORS = "__login_errors";

	/**
	 * 验证码会话标识
	 */
	public static final String SESSION_KEY_VALID_CODE = "__login_valid_code";

	/**
	 * 验证码字符数组
	 */
	public static final Character[] CHARS = new Character[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
			'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
			'Y', 'Z' }; // 验证码字符数组

	private String pattern; // 请求认证资源地址匹配模式
	private int errors = 5; // 可出错次数
	private Cache cache = new SimpleCache(); // 认证信息缓存
	private int timeout = 24 * 60 * 60; // 认证信息超时时间(秒)

	public String getPattern() {
		return pattern;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	public int getErrors() {
		return errors;
	}

	public void setErrors(int errors) {
		if (errors < 0) {
			throw new IllegalArgumentException("Illegal errors:" + errors);
		}
		this.errors = errors;
	}

	public Cache getCache() {
		return cache;
	}

	public void setCache(Cache cache) {
		this.cache = cache;
	}

	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		if (timeout < 1) {
			throw new IllegalArgumentException("Illegal timeout:" + timeout);
		}
		this.timeout = timeout;
	}

	/**
	 * 用户请求认证
	 * 
	 * @param requester
	 *            请求对象
	 */
	protected void authentication(Requester requester) {
		Token token = requester.getToken();
		if (token == null) {
			throw new TokenInvalidException("error.token.unbound");
		}
		token.validate();
		String code = (String) this.cache.get(TOKEN_CACHE_PREFIX + requester.getUser());
		if (code == null) {
			throw new TokenInvalidException("error.token.unfound");
		} else if (!code.equals(token.getCode())) {
			throw new TokenInvalidException("error.token.reset");
		}
		String permission = (String) token.get(TOKEN_KEY_PERMISSION);
		if (permission == null || !Strings.matches(requester.getUri(), permission)) {
			throw new AccessDeniedException("error.token.unauthorized");
		}
	}

	/**
	 * 用户登陆
	 * 
	 * @param requester
	 *            请求对象
	 * @param code
	 *            用户编号
	 * @param password
	 *            用户密码(明文)
	 * @param parameters
	 *            请求参数
	 * @return 令牌对象
	 */
	protected Token doLogin(final Requester requester, final String code, String password,
			Map parameters) {
		User user = Repositories.getRepository(User.class).query().eq("code", code).single();
		if (user == null || !code.equals(user.getCode())) {
			throw new AccessDeniedException("error.user.unknown");
		} else if (user.getActive() != Boolean.TRUE) {
			throw new AccessDeniedException("error.user.disabled");
		} else if (!Passwords.matches(password, user.getPassword())) {
			throw new AccessDeniedException("error.user.invalid");
		}
		Map attributes = new HashMap(1);
		if (user.getAdmin()) {
			attributes.put(TOKEN_KEY_PERMISSION, "*");
		} else if (!user.getRoles().isEmpty()) {
			Set operables = new HashSet(user.getRoles().size());
			for (Role role : user.getRoles()) {
				String operable = role.getOperable();
				if (operable != null) {
					operables.add(operable);
				}
			}
			if (!operables.isEmpty()) {
				attributes.put(TOKEN_KEY_PERMISSION, Strings.join(operables, ','));
			}
		}
		Token token = Token.build(Strings.LOCALHOST_ADDRESS, code, this.timeout, attributes);
		this.cache.set(TOKEN_CACHE_PREFIX + code, token.getCode());
		Servers.execute(new Runnable() {

			@Override
			public void run() {
				Repository repository = Repositories.getRepository(Logined.class);
				Logined logined = Beans.getInstance(repository.getModel());
				logined.setUser(code);
				logined.setHost(requester.getHost());
				logined.setSpend(System.currentTimeMillis() - requester.getCreated().getTime());
				repository.save(logined);
			}

		});
		return token;
	}

	@Override
	public void onInvokeEvent(InvokeBeforeEvent event) {
		Requester requester = event.getSource();
		if (!"people/auth/login".equals(requester.getUri()) && !"people/auth/verifycode".equals(requester.getUri())) {
			Requester root = null;
			if (this.pattern == null) {
				this.authentication(requester);
			} else if ((root = Invokes.getRootRequester(requester)) == requester) {
				if (Strings.matches(requester.getUri(), this.pattern)) {
					this.authentication(requester);
				}
			} else if (Strings.matches(root.getUri(), this.pattern)
					&& Strings.matches(requester.getUri(), this.pattern)) {
				this.authentication(requester);
			}
		}
	}

	@Override
	public void onServiceEvent(UpdateEvent event) {
		Object entity = event.getEntity();
		if (entity instanceof User) {
			User user = (User) entity;
			User original = Repositories.getRepository(User.class).get(user.getId());
			if (!Beans.isEqual(user.getActive(), original.getActive())
					|| !Beans.isEqual(user.getGroup(), original.getGroup())
					|| !Beans.isEqual(user.getRoles(), original.getRoles())) {
				this.cache.remove(TOKEN_CACHE_PREFIX + user.getCode());
			}
		}
	}

	@Override
	public byte[] verifycode(Requester requester, Map parameters) throws IOException {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		String content = Strings.random(4, CHARS);
		ImageIO.write(Opcodes.encode(content), "jpg", output);
		requester.getSession().setAttribute(SESSION_KEY_VALID_CODE, content);
		return output.toByteArray();
	}

	@Override
	public Token login(Requester requester, String code, String password, Map parameters) {
		if (this.errors == 0) {
			return this.doLogin(requester, code, password, parameters);
		}
		Session session = requester.getSession();
		Integer failed = (Integer) session.getAttribute(SESSION_KEY_ERRORS);
		if (failed != null && failed >= this.errors) {
			String verifycode = (String) requester.getParameter("verifycode");
			if (verifycode == null) {
				throw new AccessDeniedException("error.verifycode.required");
			} else if (!verifycode.equalsIgnoreCase((String) session.getAttribute(SESSION_KEY_VALID_CODE))) {
				throw new AccessDeniedException("error.verifycode.invalid");
			}
		}
		try {
			Token token = this.doLogin(requester, code, password, parameters);
			session.removeAttribute(SESSION_KEY_ERRORS);
			return token;
		} catch (Exception e) {
			if (failed == null) {
				session.setAttribute(SESSION_KEY_ERRORS, 1);
			} else if (failed < this.errors) {
				session.setAttribute(SESSION_KEY_ERRORS, failed + 1);
			}
			if (e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new RuntimeException(e);
		} finally {
			session.removeAttribute(SESSION_KEY_VALID_CODE);
		}
	}

	@Override
	public void logout(Requester requester, Map parameters) {
		this.cache.remove(TOKEN_CACHE_PREFIX + requester.getUser());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy