diff --git a/boot-service/src/main/java/com/xwbing/service/sys/SysUserService.java b/boot-service/src/main/java/com/xwbing/service/sys/SysUserService.java index 7502999..ccb3156 100644 --- a/boot-service/src/main/java/com/xwbing/service/sys/SysUserService.java +++ b/boot-service/src/main/java/com/xwbing/service/sys/SysUserService.java @@ -417,7 +417,7 @@ public class SysUserService { * @param userName * @return */ - private SysUser getByUserName(String userName) { + public SysUser getByUserName(String userName) { return sysUserRepository.getByUserName(userName); } @@ -429,15 +429,26 @@ public class SysUserService { * @param salt * @return */ - private boolean checkPassWord(String passWord, String realPassWord, String salt) { + public boolean checkPassWord(String passWord, String realPassWord, String salt) { + String validatePassWord = getSysPassWord(passWord, salt); + //判断密码是否相同 + return realPassWord.equals(validatePassWord); + } + + /** + * 获取数据库密码 + * + * @param passWord + * @param salt + * @return + */ + public String getSysPassWord(String passWord, String salt) { // 根据密码盐值 解码 byte[] saltByte = EncodeUtils.hexDecode(salt); byte[] hashPassword = Digests.sha1(passWord.getBytes(), saltByte, PassWordUtil.HASH_INTERATIONS); // 密码 数据库中密码 - String validatePassWord = EncodeUtils.hexEncode(hashPassword); - //判断密码是否相同 - return realPassWord.equals(validatePassWord); + return EncodeUtils.hexEncode(hashPassword); } /** diff --git a/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaException.java b/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaException.java new file mode 100644 index 0000000..0c0f17d --- /dev/null +++ b/boot-service/src/main/java/com/xwbing/util/captcha/CaptchaException.java @@ -0,0 +1,23 @@ +package com.xwbing.util.captcha; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/28 14:51 + * 作者: xiangwb + * 说明: + */ +public class CaptchaException extends RuntimeException { + private static final long serialVersionUID = 3106485920407116428L; + + public CaptchaException(Throwable cause) { + super(cause); + } + + public CaptchaException(String message) { + super(message); + } + + public CaptchaException(String message, Throwable cause) { + super(message, cause); + } +} 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 7567b55..c87c4f5 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 @@ -4,16 +4,13 @@ import com.alibaba.fastjson.JSONObject; import com.xwbing.annotation.LogInfo; import com.xwbing.constant.CommonConstant; import com.xwbing.constant.CommonEnum; -import com.xwbing.domain.entity.sys.SysAuthority; -import com.xwbing.domain.entity.sys.SysRole; -import com.xwbing.domain.entity.sys.SysUser; -import com.xwbing.domain.entity.sys.SysUserRole; +import com.xwbing.domain.entity.sys.*; import com.xwbing.domain.entity.vo.*; -import com.xwbing.service.sys.SysAuthorityService; -import com.xwbing.service.sys.SysRoleService; -import com.xwbing.service.sys.SysUserRoleService; -import com.xwbing.service.sys.SysUserService; +import com.xwbing.redis.RedisService; +import com.xwbing.service.sys.*; +import com.xwbing.shiro.UsernamePasswordCaptchaToken; import com.xwbing.util.CommonDataUtil; +import com.xwbing.util.IpUtil; import com.xwbing.util.JsonResult; import com.xwbing.util.RestMessage; import io.swagger.annotations.Api; @@ -21,6 +18,8 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -28,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.util.ArrayList; +import java.util.Date; import java.util.List; /** @@ -48,6 +48,10 @@ public class SysUserControl { private SysRoleService sysRoleService; @Resource private SysAuthorityService sysAuthorityService; + @Resource + private RedisService redisService; + @Resource + private SysUserLoginInOutService loginInOutService; @LogInfo("添加用户") @ApiOperation(value = "添加用户", response = RestMessageVo.class) @@ -115,6 +119,36 @@ public class SysUserControl { 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("用户名或密码不能为空"); + } + if (StringUtils.isEmpty(checkCode)) { + 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); + subject.login(token); + if (subject.isAuthenticated()) { + //保存登录信息 + SysUserLoginInOut loginInOut = new SysUserLoginInOut(); + loginInOut.setCreateTime(new Date()); + loginInOut.setUserId(user.getId()); + loginInOut.setInoutType(CommonEnum.LoginInOutEnum.IN.getValue()); + loginInOut.setIp(ip); + RestMessage save = loginInOutService.save(loginInOut); + if (!save.isSuccess()) { + return JsonResult.toJSONObj("保存用户登录日志失败"); + } + } + return JsonResult.toJSONObj(new Object(), "登录成功"); + } + @LogInfo("登出") @ApiOperation(value = "登出", response = RestMessageVo.class) @GetMapping("logout") @@ -123,6 +157,28 @@ public class SysUserControl { 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(); + if (null != sysUser) { + SysUserLoginInOut loginInOut = new SysUserLoginInOut(); + loginInOut.setCreateTime(new Date()); + loginInOut.setUserId(sysUser.getId()); + loginInOut.setInoutType(CommonEnum.LoginInOutEnum.OUT.getValue()); + loginInOut.setIp(IpUtil.getIpAddr(request)); + RestMessage out = loginInOutService.save(loginInOut); + if (out.isSuccess()) { + return JsonResult.toJSONObj("保存用户登出信息失败"); + } + } + } + return JsonResult.toJSONObj(new Object(),"登出成功"); + } + @LogInfo("修改密码") @ApiOperation(value = "修改密码", response = RestMessageVo.class) @PostMapping("updatePassWord") diff --git a/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java new file mode 100644 index 0000000..f58f1ef --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/MyShiroRealm.java @@ -0,0 +1,103 @@ +package com.xwbing.shiro; + +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.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; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.util.ByteSource; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/28 14:32 + * 作者: xiangwb + * 说明: + */ +@Component +public class MyShiroRealm extends AuthorizingRealm { + @Resource + private SysUserService sysUserService; + @Resource + private SysRoleService sysRoleService; + + /** + * 授权 + * + * @param principalCollection + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { + // 获取验证的对象 + if (principalCollection == null) { + throw new AuthorizationException("Principal对象不能为空"); + } + SysUser user = (SysUser) principalCollection.fromRealm(getName()).iterator().next(); + if (user != null) { + // 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission) + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + // 用户的角色集合 + List sysRoles = sysRoleService.listByUserIdEnable(user.getId(), "Y"); + Set roles = new HashSet<>(); + sysRoles.forEach(sysRole -> roles.add(sysRole.getName())); + info.setRoles(roles); + //用户的权限集合 + Set permissions = new HashSet<>(); + List sysAuthorities = sysUserService.listAuthorityByIdAndEnable(user.getId(), "Y"); + sysAuthorities.forEach(sysAuthority -> permissions.add(sysAuthority.getUrl())); + info.setStringPermissions(permissions); + return info; + } + return null; + } + + /** + * 验证用户身份 + * + * @param authenticationToken + * @return + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { + UsernamePasswordCaptchaToken captchaToken = (UsernamePasswordCaptchaToken) authenticationToken; + SysUser user = sysUserService.getByUserName(captchaToken.getUsername()); + if (user == null) { + throw new BusinessException("账号错误"); + } + String sysPassWord = user.getPassword(); + String salt = user.getSalt(); + boolean flag = sysUserService.checkPassWord(String.valueOf(captchaToken.getPassword()), sysPassWord, salt); + if (!flag) { + throw new BusinessException("密码错误"); + } + String captcha = captchaToken.getCaptcha(); + String exitCode = (String) SecurityUtils.getSubject().getSession() + .getAttribute(CommonConstant.KEY_CAPTCHA); + if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(exitCode)) { + throw new CaptchaException("验证码错误"); + } + 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 new file mode 100644 index 0000000..3872b4b --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/ShiroConfig.java @@ -0,0 +1,58 @@ +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/28 14:06 + * 作者: xiangwb + * 说明: + */ +@Configuration +public class ShiroConfig { + @Bean + public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); + shiroFilterFactoryBean.setSecurityManager(securityManager); + // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 + shiroFilterFactoryBean.setLoginUrl("/login"); + // 登录成功后要跳转的链接 + shiroFilterFactoryBean.setSuccessUrl("/index"); + // 未授权界面; + shiroFilterFactoryBean.setUnauthorizedUrl("/403"); + // 拦截器. + Map filterChainDefinitionMap = new LinkedHashMap<>(); + // 配置不会被拦截的链接 顺序判断 + filterChainDefinitionMap.put("/static/**", "anon"); + filterChainDefinitionMap.put("/ajaxLogin", "anon"); + // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 + filterChainDefinitionMap.put("/logout", "logout"); + filterChainDefinitionMap.put("/add", "perms[权限添加]"); + // :这是一个坑呢,一不小心代码就不好使了; + // + filterChainDefinitionMap.put("/**", "authc"); + shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); + System.out.println("Shiro拦截器工厂类注入成功"); + return shiroFilterFactoryBean; + } + + @Bean + public MyShiroRealm shiroRealm() { + return new MyShiroRealm(); + } + + + @Bean + public SecurityManager securityManager() { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(shiroRealm()); + return securityManager; + } +} diff --git a/boot-web/src/main/java/com/xwbing/shiro/UrlPermissionsFilter.java b/boot-web/src/main/java/com/xwbing/shiro/UrlPermissionsFilter.java new file mode 100644 index 0000000..f7f4afa --- /dev/null +++ b/boot-web/src/main/java/com/xwbing/shiro/UrlPermissionsFilter.java @@ -0,0 +1,12 @@ +package com.xwbing.shiro; + +import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter; + +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/28 16:42 + * 作者: xiangwb + * 说明: + */ +public class UrlPermissionsFilter extends PermissionsAuthorizationFilter { +} 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 8d725f6..0d3208d 100644 --- a/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java +++ b/boot-web/src/main/java/com/xwbing/shiro/UsernamePasswordCaptchaToken.java @@ -1,17 +1,21 @@ package com.xwbing.shiro; import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.shiro.authc.UsernamePasswordToken; +/** + * 项目名称: boot-module-demo + * 创建时间: 2018/3/28 14:44 + * 作者: xiangwb + * 说明: :扩展默认的用户认证的bean + */ +@EqualsAndHashCode(callSuper = true) @Data 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;