/*
* Copyright 2015-2016 http://hsweb.me
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hsweb.web.controller.login;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.hsweb.commons.MD5;
import org.hsweb.web.bean.po.user.User;
import org.hsweb.web.core.authorize.annotation.Authorize;
import org.hsweb.web.core.exception.AuthorizeForbiddenException;
import org.hsweb.web.core.exception.NotFoundException;
import org.hsweb.web.core.logger.annotation.AccessLogger;
import org.hsweb.web.core.message.ResponseMessage;
import org.hsweb.web.core.session.HttpSessionManager;
import org.hsweb.web.core.utils.WebUtil;
import org.hsweb.web.service.config.ConfigService;
import org.hsweb.web.service.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 授权控制器,用于登录系统
*/
@RestController
public class AuthorizeController {
/**
* 授权过程所需缓存
*/
@Autowired(required = false)
private CacheManager cacheManager;
/**
* 用户服务类
*/
@Resource
private UserService userService;
/**
* 配置服务类
*/
@Resource
private ConfigService configService;
/**
* httpSession管理器
*/
@Autowired
private HttpSessionManager httpSessionManager;
/**
* 获取当前在线人数
*
* @return 当前在线人数
*/
@RequestMapping(value = "/online/total", method = RequestMethod.GET)
@AccessLogger("当前在线总人数")
@Authorize
public ResponseMessage onlineTotal() {
return ResponseMessage.ok(httpSessionManager.getUserTotal());
}
/**
* 获取当前在线用户ID集合
*
* @return 在线用户ID集合
*/
@RequestMapping(value = "/online", method = RequestMethod.GET)
@AccessLogger("当前在线用户ID")
@Authorize
public ResponseMessage online() {
return ResponseMessage.ok(httpSessionManager.getUserIdList());
}
/**
* 获取当前在线用户信息集合
*
* @return 在线用户信息集合
*/
@RequestMapping(value = "/online/list", method = RequestMethod.GET)
@AccessLogger("当前在线用户")
@Authorize
public ResponseMessage onlineInfo() {
return ResponseMessage.ok(httpSessionManager.tryGetAllUser())
.include(User.class, "id", "username", "name", "phone", "email");
}
/**
* 退出登录
*
* @return 退出成功
*/
@RequestMapping(value = "/exit", method = RequestMethod.POST)
@AccessLogger("登出")
public ResponseMessage exit(HttpSession session) {
User user = WebUtil.getLoginUser();
if (user != null) {
httpSessionManager.removeUser(user.getId());
//使用redis时,有时候removeUser会失效,removeSession总可以了吧
httpSessionManager.removeSession(session.getId());
}
return ResponseMessage.ok();
}
/**
* 用户登录,如果密码输出错误,将会限制登录.
* <ul>
* <li>
* 密码最大错误次数从配置中获取{@link ConfigService#getInt(String, String, int)}:login,error.max_number,5
* </li>
* <li>
* 禁止登录分钟数从配置中获取{@link ConfigService#getInt(String, String, int)}:login,error.wait_minutes,10
* <p>
* </li>
* </ul>
*
* @param username 用户名
* @param password 密码
* @param request {@link HttpServletRequest}
* @return 登录情况
* @throws AuthorizeForbiddenException 用户被锁定或者密码错误
* @throws NotFoundException 用户不存在或已注销
* @throws Exception 其他错误
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
@AccessLogger("登录")
public ResponseMessage login(@RequestParam String username,
@RequestParam String password,
HttpServletRequest request) throws Exception {
//判断用户是否多次输入密码错误
String userIp = WebUtil.getIpAddr(request);
int maxErrorNumber = configService.getInt("login", "error.max_number", 5);
int waitMinutes = configService.getInt("login", "error.wait_minutes", 10);
Cache cache = cacheManager.getCache("login.error");
String cachePrefix = username.concat("@").concat(userIp);
String timeCacheKey = cachePrefix.concat("-time");
String numberCacheKey = cachePrefix.concat("-number");
Integer error_number = cache.get(numberCacheKey, Integer.class);
Long error_time = cache.get(timeCacheKey, Long.class);
long now_time = System.currentTimeMillis();
if (error_number != null && error_time != null) {
if ((now_time - error_time) / 1000 / 60d > waitMinutes) {
cache.evict(timeCacheKey);
cache.evict(numberCacheKey);
error_number = 0;
error_time = 0l;
}
if (error_number >= maxErrorNumber)
throw new AuthorizeForbiddenException("您的账户已被锁定登录,请" + (waitMinutes - ((now_time - error_time) / 1000 / 60)) + "分钟后再试!");
}
User user = userService.selectByUserName(username);
if (user == null || user.getStatus() != 1) throw new NotFoundException("用户不存在或已注销");
//密码错误
if (!user.getPassword().equals(MD5.encode(password))) {
if (error_number == null) error_number = 0;
cache.put(timeCacheKey, System.currentTimeMillis());
cache.put(numberCacheKey, ++error_number);
throw new AuthorizeForbiddenException("密码错误,你还可以重试" + (maxErrorNumber - error_number) + "次");
}
cache.evict(timeCacheKey);
cache.evict(numberCacheKey);
user.setPassword("");//去除密码
if (user.getUsername().equals("admin"))
userService.initAdminUser(user);
else
user.initRoleInfo();
User newUser = new User();
BeanUtilsBean.getInstance().getPropertyUtils()
.copyProperties(newUser, user);
httpSessionManager.addUser(newUser, request.getSession());
return ResponseMessage.ok();
}
@PostConstruct
public void init() {
//如果系统没有配置cacheManager,则使用ConcurrentMapCacheManager
if (cacheManager == null) {
cacheManager = new ConcurrentMapCacheManager();
}
}
}