From d4063ad719e120df496173154eeb75578e2f3419 Mon Sep 17 00:00:00 2001 From: xiangwb Date: Fri, 30 Mar 2018 10:51:27 +0800 Subject: [PATCH] merge --- .../xwbing/util/captcha/CaptchaServlet.java | 9 ++- .../src/main/resources/druid.properties | 2 +- .../DispatcherServletConfig.java | 3 +- .../xwbing/controller/sys/SysUserControl.java | 29 +------- .../handler/GlobalExceptionHandler.java | 26 ++++++- .../com/xwbing/handler/LoginInterceptor.java | 70 ------------------- .../FormAuthenticationCaptchaFilter.java | 38 ++++++++++ .../com/xwbing/shiro/MySessionManager.java | 41 +++++++++++ .../java/com/xwbing/shiro/MyShiroRealm.java | 24 +++---- .../java/com/xwbing/shiro/ShiroConfig.java | 56 ++++++++------- .../shiro/UsernamePasswordCaptchaToken.java | 5 ++ boot-web/src/main/resources/shiro.properties | 7 ++ 12 files changed, 167 insertions(+), 143 deletions(-) delete mode 100644 boot-web/src/main/java/com/xwbing/handler/LoginInterceptor.java create mode 100644 boot-web/src/main/java/com/xwbing/shiro/FormAuthenticationCaptchaFilter.java create mode 100644 boot-web/src/main/java/com/xwbing/shiro/MySessionManager.java create mode 100644 boot-web/src/main/resources/shiro.properties diff --git a/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaServlet.java b/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaServlet.java index 243b460..2814ebb 100644 --- a/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaServlet.java +++ b/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaServlet.java @@ -1,7 +1,6 @@ package com.xwbing.util.captcha; import com.xwbing.constant.CommonConstant; -import com.xwbing.util.CommonDataUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,6 +8,7 @@ import javax.imageio.ImageIO; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; @@ -33,10 +33,9 @@ public class CaptchaServlet extends HttpServlet { CaptchaUtil tool = new CaptchaUtil(); StringBuffer code = new StringBuffer(); BufferedImage image = tool.genRandomCodeImage(code); -// HttpSession session = req.getSession(); -// session.removeAttribute(CommonConstant.KEY_CAPTCHA); -// session.setAttribute(CommonConstant.KEY_CAPTCHA, code.toString()); - CommonDataUtil.setToken(CommonConstant.KEY_CAPTCHA, code.toString()); + HttpSession session = req.getSession(); + session.removeAttribute(CommonConstant.KEY_CAPTCHA); + session.setAttribute(CommonConstant.KEY_CAPTCHA, code.toString()); // 将内存中的图片通过流形式输出到客户端 OutputStream out = res.getOutputStream(); ImageIO.write(image, "JPEG", out); diff --git a/boot-service/src/main/resources/druid.properties b/boot-service/src/main/resources/druid.properties index 56026a5..83f4cbf 100644 --- a/boot-service/src/main/resources/druid.properties +++ b/boot-service/src/main/resources/druid.properties @@ -2,7 +2,7 @@ jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/springboot2?useUnicode=true&characterEncoding=utf8 jdbc.username=root -jdbc.password= +jdbc.password=xwbing # 初始化大小,最小,最大 jdbc.initSize=1 jdbc.minIdle=3 diff --git a/boot-web/src/main/java/com/xwbing/configuration/DispatcherServletConfig.java b/boot-web/src/main/java/com/xwbing/configuration/DispatcherServletConfig.java index 9d1b83c..c333cac 100644 --- a/boot-web/src/main/java/com/xwbing/configuration/DispatcherServletConfig.java +++ b/boot-web/src/main/java/com/xwbing/configuration/DispatcherServletConfig.java @@ -3,7 +3,6 @@ package com.xwbing.configuration; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; -import com.xwbing.handler.LoginInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @@ -29,7 +28,7 @@ public class DispatcherServletConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { //拦截器1:登录判断 - registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**"); +// registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**"); // registry.addInterceptor(new LoginInterceptor()) // .addPathPatterns("/**").excludePathPatterns("/user/login", "/servlet/captchaCode", "/swagger-ui.html"); //拦截器2... diff --git a/boot-web/src/main/java/com/xwbing/controller/sys/SysUserControl.java b/boot-web/src/main/java/com/xwbing/controller/sys/SysUserControl.java index 1c91628..7538ed9 100644 --- a/boot-web/src/main/java/com/xwbing/controller/sys/SysUserControl.java +++ b/boot-web/src/main/java/com/xwbing/controller/sys/SysUserControl.java @@ -108,25 +108,10 @@ public class SysUserControl { @LogInfo("登录") @ApiOperation(value = "登录", response = RestMessageVo.class) @PostMapping("login") - public JSONObject login(HttpServletRequest request, @RequestParam String userName, @RequestParam String passWord, @RequestParam String checkCode) { + public JSONObject login(HttpServletRequest request, @RequestParam String userName, @RequestParam String passWord, @RequestParam String checkCode, boolean rememberMe) { if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(passWord)) { return JsonResult.toJSONObj("用户名或密码不能为空"); } - if (StringUtils.isEmpty(checkCode)) { - return JsonResult.toJSONObj("请输入验证码"); - } - RestMessage login = sysUserService.login(request, userName, passWord, checkCode); - return JsonResult.toJSONObj(login); - } - - @LogInfo("登录") - @ApiOperation(value = "登录", response = RestMessageVo.class) - @PostMapping("login2") - public JSONObject login2(HttpServletRequest request, @RequestParam String userName, @RequestParam String passWord, @RequestParam String checkCode, boolean rememberMe) { - if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(passWord)) { - return JsonResult.toJSONObj("用户名或密码不能为空"); - } - SysUser user = sysUserService.getByUserName(userName); Subject subject = SecurityUtils.getSubject(); String ip = IpUtil.getIpAddr(request); UsernamePasswordCaptchaToken token = new UsernamePasswordCaptchaToken(userName, passWord.toCharArray(), rememberMe, ip, checkCode); @@ -135,7 +120,7 @@ public class SysUserControl { //保存登录信息 SysUserLoginInOut loginInOut = new SysUserLoginInOut(); loginInOut.setCreateTime(new Date()); - loginInOut.setUserId(user.getId()); + loginInOut.setUserId(""); loginInOut.setInoutType(CommonEnum.LoginInOutEnum.IN.getValue()); loginInOut.setIp(ip); RestMessage save = loginInOutService.save(loginInOut); @@ -150,14 +135,6 @@ public class SysUserControl { @ApiOperation(value = "登出", response = RestMessageVo.class) @GetMapping("logout") public JSONObject logout(HttpServletRequest request) { - RestMessage logout = sysUserService.logout(request); - return JsonResult.toJSONObj(logout); - } - - @LogInfo("登出") - @ApiOperation(value = "登出", response = RestMessageVo.class) - @GetMapping("logout2") - public JSONObject logout2(HttpServletRequest request) { Subject subject = SecurityUtils.getSubject(); if (subject != null && subject.getPrincipals() != null) { SysUser sysUser = (SysUser) subject.getPrincipals().getPrimaryPrincipal(); @@ -173,7 +150,7 @@ public class SysUserControl { } } } - return JsonResult.toJSONObj(new Object(),"登出成功"); + return JsonResult.toJSONObj(new Object(), "登出成功"); } @LogInfo("修改密码") diff --git a/boot-web/src/main/java/com/xwbing/handler/GlobalExceptionHandler.java b/boot-web/src/main/java/com/xwbing/handler/GlobalExceptionHandler.java index 06207d7..c767395 100644 --- a/boot-web/src/main/java/com/xwbing/handler/GlobalExceptionHandler.java +++ b/boot-web/src/main/java/com/xwbing/handler/GlobalExceptionHandler.java @@ -3,6 +3,8 @@ package com.xwbing.handler; import com.alibaba.fastjson.JSONObject; import com.xwbing.exception.BusinessException; import com.xwbing.util.JsonResult; +import com.xwbing.util.captcha.CaptchaException; +import org.apache.shiro.authc.AuthenticationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.DefaultMessageSourceResolvable; @@ -50,11 +52,19 @@ public class GlobalExceptionHandler { } /** - * CompletableFuture完成结果或任务过程中出现的异常 + * shiro登录认证 * * @param ex * @return */ + @ExceptionHandler(value = AuthenticationException.class) + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + public JSONObject handlerAuthenticationException(AuthenticationException ex) { + logger.error(ex.getMessage()); + return JsonResult.toJSONObj(ex.getMessage()); + } + @ExceptionHandler(value = CompletionException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody @@ -69,6 +79,20 @@ public class GlobalExceptionHandler { return JsonResult.toJSONObj(errorMessages); } + /** + * 验证码异常 + * + * @param ex + * @return + */ + @ExceptionHandler(value = CaptchaException.class) + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + public JSONObject handlerCaptchaException(CaptchaException ex) { + logger.error(ex.getMessage()); + return JsonResult.toJSONObj(ex.getMessage()); + } + /** * 表单检验(validator) 异常 * diff --git a/boot-web/src/main/java/com/xwbing/handler/LoginInterceptor.java b/boot-web/src/main/java/com/xwbing/handler/LoginInterceptor.java deleted file mode 100644 index 05c4735..0000000 --- a/boot-web/src/main/java/com/xwbing/handler/LoginInterceptor.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.xwbing.handler; - -import com.alibaba.fastjson.JSON; -import com.xwbing.configuration.DispatcherServletConfig; -import com.xwbing.constant.CommonConstant; -import com.xwbing.util.CommonDataUtil; -import com.xwbing.util.RestMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.OutputStream; -import java.util.HashSet; -import java.util.Set; - -/** - * 说明: 登录拦截器 - * 项目名称: boot-module-demo - * 创建时间: 2017/5/10 16:36 - * 作者: xiangwb - */ -public class LoginInterceptor extends HandlerInterceptorAdapter { - private final Logger logger = LoggerFactory.getLogger(DispatcherServletConfig.class); - private static final Set set = new HashSet<>();//拦截器白名单 - - static { - //映射到登录页面,不拦截 - set.add("/"); - //映射swagger文档 - set.add("/doc"); - //验证码 - set.add("/captcha"); - //swagger - set.add("/v2/api-docs"); - set.add("/swagger-resources"); - set.add("/configuration/ui"); - set.add("/configuration/security"); - //德鲁伊监控 - set.add("/druid"); - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String servletPath = request.getServletPath(); - if (!set.contains(servletPath) && !servletPath.contains("login") && !servletPath.contains("test")) { -// HttpSession session = request.getSession(); -// if (session.getAttribute(CommonConstant.CURRENT_USER)!=null) { -// return true; -// }else { -// return false; -// } - if (CommonDataUtil.getToken(CommonConstant.CURRENT_USER) != null) { - return true; - } else { - logger.info("用户未登录"); - OutputStream outputStream = response.getOutputStream(); - RestMessage restMessage = new RestMessage(); - restMessage.setMessage("请先登录"); - outputStream.write(JSON.toJSONString(restMessage).getBytes("utf-8")); - outputStream.close(); - //未登录,重定向到登录页面 -// response.sendRedirect("/login.html"); - return false; - } - } - return true; - } -} diff --git a/boot-web/src/main/java/com/xwbing/shiro/FormAuthenticationCaptchaFilter.java b/boot-web/src/main/java/com/xwbing/shiro/FormAuthenticationCaptchaFilter.java new file mode 100644 index 0000000..7666f9c --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/FormAuthenticationCaptchaFilter.java @@ -0,0 +1,38 @@ +package com.xwbing.shiro; + +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; +import org.apache.shiro.web.util.WebUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/29 15:28 + * 作者: xiangwb + * 说明: + */ +public class FormAuthenticationCaptchaFilter extends FormAuthenticationFilter { + public static final String DEFAULT_CAPTCHA_PARAM = "captcha_code"; + private String captchaParam = DEFAULT_CAPTCHA_PARAM; + + public String getCaptchaParam() { + return captchaParam; + } + + protected String getCaptcha(ServletRequest request) { + return WebUtils.getCleanParam(request, getCaptchaParam()); + + } + + @Override + protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { + String username = getUsername(request); + String password = getPassword(request); + String captcha = getCaptcha(request); + boolean rememberMe = isRememberMe(request); + String host = getHost(request); + return new UsernamePasswordCaptchaToken(username, password.toCharArray(), rememberMe, host, captcha); + } +} diff --git a/boot-web/src/main/java/com/xwbing/shiro/MySessionManager.java b/boot-web/src/main/java/com/xwbing/shiro/MySessionManager.java new file mode 100644 index 0000000..7e3505b --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/MySessionManager.java @@ -0,0 +1,41 @@ +package com.xwbing.shiro; + +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.web.servlet.ShiroHttpServletRequest; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.apache.shiro.web.util.WebUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.Serializable; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/29 16:29 + * 作者: xiangwb + * 说明: + */ +public class MySessionManager extends DefaultWebSessionManager { + private static final String AUTHORIZATION = "Authorization"; + + private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; + + public MySessionManager() { + super(); + } + + @Override + protected Serializable getSessionId(ServletRequest request, ServletResponse response) { + String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION); + //如果请求头中有 Authorization 则其值为sessionId + if (StringUtils.isNotEmpty(id)) { + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); + return id; + } else { + //否则按默认规则从cookie取sessionId + return super.getSessionId(request, response); + } + } +} diff --git a/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java index bff36d4..31da822 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java +++ b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java @@ -4,12 +4,9 @@ import com.xwbing.constant.CommonConstant; import com.xwbing.domain.entity.sys.SysAuthority; import com.xwbing.domain.entity.sys.SysRole; import com.xwbing.domain.entity.sys.SysUser; -import com.xwbing.exception.BusinessException; import com.xwbing.service.sys.SysRoleService; import com.xwbing.service.sys.SysUserService; -import com.xwbing.util.CommonDataUtil; -import com.xwbing.util.captcha.CaptchaException; -import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; @@ -72,7 +69,7 @@ public class MyShiroRealm extends AuthorizingRealm { } /** - * 认证.登录 + * 登录.认证 * * @param authenticationToken * @return @@ -81,22 +78,21 @@ public class MyShiroRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordCaptchaToken captchaToken = (UsernamePasswordCaptchaToken) authenticationToken; + String captcha = captchaToken.getCaptcha(); + String imgCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CommonConstant.KEY_CAPTCHA); +// if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) { +// throw new AuthenticationException("验证码错误"); +// } SysUser user = sysUserService.getByUserName(captchaToken.getUsername()); if (user == null) { - throw new BusinessException("账号错误"); + throw new AuthenticationException("账号或密码错误"); } String sysPassWord = user.getPassword(); String salt = user.getSalt(); boolean flag = sysUserService.checkPassWord(String.valueOf(captchaToken.getPassword()), sysPassWord, salt); if (!flag) { - throw new BusinessException("密码错误"); + throw new AuthenticationException("账号或密码错误"); } - String captcha = captchaToken.getCaptcha(); - String imgCode = (String) CommonDataUtil.getToken(CommonConstant.KEY_CAPTCHA); - if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) { - throw new CaptchaException("验证码错误"); - } - return new SimpleAuthenticationInfo(user, sysPassWord, - ByteSource.Util.bytes(salt), getName()); + return new SimpleAuthenticationInfo(user, sysPassWord, ByteSource.Util.bytes(salt), getName()); } } diff --git a/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java b/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java index e7bfbfe..82faa8d 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java +++ b/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java @@ -3,8 +3,10 @@ package com.xwbing.shiro; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import java.util.LinkedHashMap; import java.util.Map; @@ -16,37 +18,43 @@ import java.util.Map; * 说明: anon,authc */ @Configuration +@PropertySource("classpath:shiro.properties") public class ShiroConfig { + @Value("${rememberMeCookieMaxAge}") + private Integer rememberMeCookieMaxAge; + @Value("${globalSessionTimeout}") + private Long globalSessionTimeout; + @Value("${sessionValidationInterval}") + private String sessionValidationInterval; + @Value("${sessionsCacheName}") + private String sessionsCacheName; + @Value("${sessionIdCookieName}") + private String sessionIdCookieName; + @Bean - public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { + public ShiroFilterFactoryBean shirFilter() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); - shiroFilterFactoryBean.setSecurityManager(securityManager); - //配置登录的url和登录成功的url - shiroFilterFactoryBean.setLoginUrl("/login"); - shiroFilterFactoryBean.setSuccessUrl("/index"); - // 未授权界面; - shiroFilterFactoryBean.setUnauthorizedUrl("/403"); + shiroFilterFactoryBean.setSecurityManager(securityManager()); //配置访问权限 Map chains = new LinkedHashMap<>(); // 配置不会被拦截的链接 顺序判断 - chains.put("/favicon.ico", "anon"); - chains.put("/static/**", "anon"); - //映射到登录页面,不拦截 - chains.put("/","anon"); - //映射swagger文档 - chains.put("/doc","anon"); - //验证码 - chains.put("/captcha","anon"); - //swagger - chains.put("/v2/api-docs","anon"); - chains.put("/swagger-resources","anon"); - chains.put("/configuration/ui","anon"); - chains.put("/configuration/security","anon"); - //德鲁伊监控 - chains.put("/druid","anon"); - chains.put("/**", "authc"); + //配置登录的url和登录成功的url + shiroFilterFactoryBean.setLoginUrl("/user/login"); +// shiroFilterFactoryBean.setSuccessUrl("/index"); + // 未授权界面; + shiroFilterFactoryBean.setUnauthorizedUrl("/403"); +// chains.put("/favicon.ico", "anon"); +// chains.put("/static/**", "anon"); +// chains.put("/", "anon"); +// chains.put("/doc", "anon"); +// chains.put("/captcha", "anon"); +// chains.put("/v2/api-docs", "anon"); +// chains.put("/swagger-resources", "anon"); +// chains.put("/configuration/ui", "anon"); +// chains.put("/configuration/security", "anon"); +// chains.put("/druid", "anon"); +// chains.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(chains); - System.out.println("Shiro拦截器工厂类注入成功"); return shiroFilterFactoryBean; } diff --git a/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java b/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java index 0d3208d..10bd979 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java +++ b/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java @@ -16,6 +16,11 @@ public class UsernamePasswordCaptchaToken extends UsernamePasswordToken { private static final long serialVersionUID = 6059396037971862504L; private String captcha; + public UsernamePasswordCaptchaToken() { + super(); + + } + public UsernamePasswordCaptchaToken(String username, char[] password, boolean rememberMe, String host, String captcha) { super(username, password, rememberMe, host); this.captcha = captcha; diff --git a/boot-web/src/main/resources/shiro.properties b/boot-web/src/main/resources/shiro.properties new file mode 100644 index 0000000..49c04a9 --- /dev/null +++ b/boot-web/src/main/resources/shiro.properties @@ -0,0 +1,7 @@ +### s +rememberMeCookieMaxAge=604800 +globalSessionTimeout=1800000 +sessionValidationInterval=60000 +sessionsCacheName=shiro-activeSessionCache +sessionIdCookieName=drore.session.ywjpjScenic.id +path=/ \ No newline at end of file