diff --git a/src/main/java/com/objecteye/config/AuthenticationHeadFilter.java b/src/main/java/com/objecteye/config/AuthenticationHeadFilter.java index 67f99a2..984f2af 100644 --- a/src/main/java/com/objecteye/config/AuthenticationHeadFilter.java +++ b/src/main/java/com/objecteye/config/AuthenticationHeadFilter.java @@ -1,7 +1,10 @@ package com.objecteye.config; import com.alibaba.fastjson.JSON; +import com.objecteye.pojo.AuthenticationToken; import com.objecteye.pojo.TokenUser; +import com.objecteye.utils.GlobalUtil; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; @@ -22,6 +25,12 @@ public class AuthenticationHeadFilter extends OncePerRequestFilter { private RsaVerifier rsaVerifier; + private RedisTemplate redisTemplate; + + public void setRedisTemplate(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + public void setRsaVerifier(RsaVerifier rsaVerifier) { this.rsaVerifier = rsaVerifier; } @@ -33,12 +42,25 @@ public class AuthenticationHeadFilter extends OncePerRequestFilter { filterChain.doFilter(httpServletRequest, httpServletResponse); return; } - TokenUser user; try { Jwt jwt = JwtHelper.decodeAndVerify(token, rsaVerifier); String claims = jwt.getClaims(); user = JSON.parseObject(claims, TokenUser.class); + // 是否单点登录 + Long createTime = user.getCreateTime(); + Long tokenCreateTime = (Long) redisTemplate.opsForHash().get(GlobalUtil.LOGIN_USER_TOKEN, user.getUsername()); + if (!createTime.equals(tokenCreateTime)) { + httpServletResponse.setContentType("application/json;charset=UTF-8"); + httpServletResponse.getWriter().write("用户已在其他地方登录"); + return; + } + // 是否超时(默认1小时) + if (System.currentTimeMillis() > createTime + 1000 * 60 * 60) { + httpServletResponse.setContentType("application/json;charset=UTF-8"); + httpServletResponse.getWriter().write("Token已过期请重新登录"); + return; + } } catch (Exception e) { e.printStackTrace(); httpServletResponse.setContentType("application/json;charset=UTF-8"); diff --git a/src/main/java/com/objecteye/config/AuthenticationLoginFilter.java b/src/main/java/com/objecteye/config/AuthenticationLoginFilter.java index fb715ea..6d1d744 100644 --- a/src/main/java/com/objecteye/config/AuthenticationLoginFilter.java +++ b/src/main/java/com/objecteye/config/AuthenticationLoginFilter.java @@ -1,5 +1,6 @@ package com.objecteye.config; +import com.objecteye.pojo.AuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; diff --git a/src/main/java/com/objecteye/config/AuthenticationProviderConfig.java b/src/main/java/com/objecteye/config/AuthenticationProviderConfig.java index 2e6d6ba..2bafec4 100644 --- a/src/main/java/com/objecteye/config/AuthenticationProviderConfig.java +++ b/src/main/java/com/objecteye/config/AuthenticationProviderConfig.java @@ -1,5 +1,6 @@ package com.objecteye.config; +import com.objecteye.pojo.AuthenticationToken; import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; diff --git a/src/main/java/com/objecteye/config/WebSecurityConfig.java b/src/main/java/com/objecteye/config/WebSecurityConfig.java index 9e5e9da..d53b69c 100644 --- a/src/main/java/com/objecteye/config/WebSecurityConfig.java +++ b/src/main/java/com/objecteye/config/WebSecurityConfig.java @@ -1,10 +1,13 @@ package com.objecteye.config; import com.objecteye.common.ResultCode; +import com.objecteye.handle.LoginFailureHandler; +import com.objecteye.handle.LoginSuccessHandler; import com.objecteye.service.impl.UserDetailServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; @@ -26,6 +29,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailServiceImpl userDetailService; + @Autowired + private RedisTemplate redisTemplate; + @Override protected void configure(HttpSecurity http) throws Exception { AuthenticationLoginFilter authenticationLoginFilter = new AuthenticationLoginFilter(); @@ -33,6 +39,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { LoginSuccessHandler loginSuccessHandler = new LoginSuccessHandler(); loginSuccessHandler.setSigner(signer); + loginSuccessHandler.setRedisTemplate(redisTemplate); authenticationLoginFilter.setAuthenticationSuccessHandler(loginSuccessHandler); authenticationLoginFilter.setAuthenticationFailureHandler(new LoginFailureHandler()); @@ -42,6 +49,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { AuthenticationHeadFilter headFilter = new AuthenticationHeadFilter(); headFilter.setRsaVerifier(verifier); + headFilter.setRedisTemplate(redisTemplate); http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { response.setContentType("application/json;charset=UTF-8"); diff --git a/src/main/java/com/objecteye/controller/UserController.java b/src/main/java/com/objecteye/controller/UserController.java index f4d4087..598a8e8 100644 --- a/src/main/java/com/objecteye/controller/UserController.java +++ b/src/main/java/com/objecteye/controller/UserController.java @@ -4,6 +4,7 @@ import com.objecteye.common.CommonResult; import com.objecteye.service.UserServices; import com.objecteye.utils.GlobalUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.Map; @@ -16,6 +17,11 @@ public class UserController extends BasicController { @Autowired public UserServices userServices; + @RequestMapping(value = "whoami", method = RequestMethod.POST, produces = GlobalUtil.COMMON_HEADER_CONTENT_TYPE) + public CommonResult whoAmI() { + return CommonResult.success(SecurityContextHolder.getContext().getAuthentication().getPrincipal()); + } + @RequestMapping(value = "checkUser", method = RequestMethod.POST, produces = GlobalUtil.COMMON_HEADER_CONTENT_TYPE) public CommonResult checkUser(@RequestBody Map requestMap) { return jsonObjectResultHandle(userServices.checkUser(requestMap)); diff --git a/src/main/java/com/objecteye/config/LoginFailureHandler.java b/src/main/java/com/objecteye/handle/LoginFailureHandler.java index 012b9cb..e5d5b7e 100644 --- a/src/main/java/com/objecteye/config/LoginFailureHandler.java +++ b/src/main/java/com/objecteye/handle/LoginFailureHandler.java @@ -1,4 +1,4 @@ -package com.objecteye.config; +package com.objecteye.handle; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; diff --git a/src/main/java/com/objecteye/config/LoginSuccessHandler.java b/src/main/java/com/objecteye/handle/LoginSuccessHandler.java index d423b05..af6f929 100644 --- a/src/main/java/com/objecteye/config/LoginSuccessHandler.java +++ b/src/main/java/com/objecteye/handle/LoginSuccessHandler.java @@ -1,6 +1,9 @@ -package com.objecteye.config; +package com.objecteye.handle; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.objecteye.utils.GlobalUtil; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.Authentication; import org.springframework.security.jwt.JwtHelper; import org.springframework.security.jwt.crypto.sign.RsaSigner; @@ -17,10 +20,15 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private RsaSigner signer; + private RedisTemplate redisTemplate; + @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { response.setContentType("application/json;charset=UTF-8"); String userJsonStr = JSON.toJSONString(authentication.getPrincipal()); + JSONObject userJson = JSON.parseObject(userJsonStr); + // 保存登录存根 + redisTemplate.opsForHash().put(GlobalUtil.LOGIN_USER_TOKEN, userJson.getString("username"), userJson.getLongValue("createTime")); String token = JwtHelper.encode(userJsonStr, signer).getEncoded(); //签发token response.getWriter().write("token=" + token); @@ -29,4 +37,8 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { public void setSigner(RsaSigner signer) { this.signer = signer; } + + public void setRedisTemplate(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } } diff --git a/src/main/java/com/objecteye/config/AuthenticationToken.java b/src/main/java/com/objecteye/pojo/AuthenticationToken.java index 5ae9088..8f8c7b9 100644 --- a/src/main/java/com/objecteye/config/AuthenticationToken.java +++ b/src/main/java/com/objecteye/pojo/AuthenticationToken.java @@ -1,4 +1,4 @@ -package com.objecteye.config; +package com.objecteye.pojo; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; diff --git a/src/main/java/com/objecteye/pojo/TokenUser.java b/src/main/java/com/objecteye/pojo/TokenUser.java index c566ff5..f2fc46b 100644 --- a/src/main/java/com/objecteye/pojo/TokenUser.java +++ b/src/main/java/com/objecteye/pojo/TokenUser.java @@ -17,11 +17,14 @@ public class TokenUser implements UserDetails { private String password; + private Long createTime; + private List simpleGrantedAuthorities; public TokenUser(String userName, String password, String... roles) { this.userName = userName; this.password = password; + this.createTime = System.currentTimeMillis(); this.simpleGrantedAuthorities = Arrays.stream(roles).map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @@ -74,4 +77,12 @@ public class TokenUser implements UserDetails { public void setSimpleGrantedAuthorities(List simpleGrantedAuthorities) { this.simpleGrantedAuthorities = simpleGrantedAuthorities; } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } } diff --git a/src/main/java/com/objecteye/pojo/UserGroup.java b/src/main/java/com/objecteye/pojo/UserGroup.java index ddcf16c..884d36f 100644 --- a/src/main/java/com/objecteye/pojo/UserGroup.java +++ b/src/main/java/com/objecteye/pojo/UserGroup.java @@ -25,7 +25,7 @@ public class UserGroup implements Serializable { private String groupLevel; /** - * 父级组织id(根为-1, 备用) + * 父级组织id(根为-1) */ private String parentCode; diff --git a/src/main/java/com/objecteye/config/AccessConfirmServiceImpl.java b/src/main/java/com/objecteye/service/impl/AccessConfirmServiceImpl.java index f90e732..077c7cb 100644 --- a/src/main/java/com/objecteye/config/AccessConfirmServiceImpl.java +++ b/src/main/java/com/objecteye/service/impl/AccessConfirmServiceImpl.java @@ -1,4 +1,4 @@ -package com.objecteye.config; +package com.objecteye.service.impl; import com.objecteye.pojo.SpecialAuthenticationUrlConfig; import com.objecteye.pojo.UserDetailsMsg; @@ -28,6 +28,16 @@ public class AccessConfirmServiceImpl { private AntPathMatcher antPathMatcher = new AntPathMatcher(); public boolean hasPermission(HttpServletRequest request, Authentication auth) { + // 不需要权限的接口 + List permitAll = new ArrayList<>(); + permitAll.add("/login"); + permitAll.add("/vehicle/user/addUser"); + + String requestUri = request.getRequestURI(); + if (permitAll.contains(requestUri)) { + return true; + } + // 匿名token不允许访问所有的接口 if (auth instanceof AnonymousAuthenticationToken) { return false; @@ -36,7 +46,7 @@ public class AccessConfirmServiceImpl { UserDetails user = (UserDetails) auth.getPrincipal(); Map specialUrlAccessMap = queryUrlByUserName(user.getUsername()); for (Map.Entry entry : specialUrlAccessMap.entrySet()) { - if (antPathMatcher.match(entry.getKey(), request.getRequestURI())) { + if (antPathMatcher.match(entry.getKey(), requestUri)) { return entry.getValue(); } } diff --git a/src/main/java/com/objecteye/service/impl/UserDetailServiceImpl.java b/src/main/java/com/objecteye/service/impl/UserDetailServiceImpl.java index dc6b29c..b10bd19 100644 --- a/src/main/java/com/objecteye/service/impl/UserDetailServiceImpl.java +++ b/src/main/java/com/objecteye/service/impl/UserDetailServiceImpl.java @@ -2,6 +2,7 @@ package com.objecteye.service.impl; import com.objecteye.pojo.TokenUser; import com.objecteye.pojo.UserDetailsMsg; +import com.objecteye.pojo.UserGroup; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; @@ -12,8 +13,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; -import java.util.List; - @Component public class UserDetailServiceImpl implements UserDetailsService { @@ -29,19 +28,19 @@ public class UserDetailServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { - List userDetailsMsgs = mongoTemplate.find(Query.query(Criteria.where("userName").is(s)), UserDetailsMsg.class); - - // admin - UserDetailsMsg superAdminUser = new UserDetailsMsg(); - superAdminUser.setUsername("superAdmin"); - superAdminUser.setPassword("123456"); - superAdminUser.setUserRole("1"); - userDetailsMsgs.add(superAdminUser); - if (userDetailsMsgs.size() > 0) { - UserDetailsMsg userDetailsMsg = userDetailsMsgs.get(0); - return new TokenUser(userDetailsMsg.getUsername(), passwordEncoder.encode(userDetailsMsg.getPassword())); + if ("superAdmin".equals(s)) { + return new TokenUser("superAdmin", passwordEncoder.encode("123456"), "1"); + } + UserDetailsMsg userDetailsMsg = mongoTemplate.findOne(Query.query(Criteria.where("username").is(s)), UserDetailsMsg.class); + if (userDetailsMsg != null) { + UserGroup userGroup = mongoTemplate.findOne(Query.query(Criteria.where("groupCode").is(userDetailsMsg.getGroup())), UserGroup.class); + String userRole = userDetailsMsg.getUserRole(); + if (userGroup != null) { + String groupLevel = userGroup.getGroupLevel(); + userRole = String.valueOf(Math.min(Integer.parseInt(userRole), Integer.parseInt(groupLevel))); + } + return new TokenUser(userDetailsMsg.getUsername(), passwordEncoder.encode(userDetailsMsg.getPassword()), userRole); } - return null; } } diff --git a/src/main/java/com/objecteye/service/impl/UserGroupServiceImpl.java b/src/main/java/com/objecteye/service/impl/UserGroupServiceImpl.java index 707a5a0..bcec611 100644 --- a/src/main/java/com/objecteye/service/impl/UserGroupServiceImpl.java +++ b/src/main/java/com/objecteye/service/impl/UserGroupServiceImpl.java @@ -66,7 +66,14 @@ public class UserGroupServiceImpl implements IUserGroupService { @Override public JSONObject addGroup(Map requestMap) { UserGroup userGroup = JSON.parseObject(JSON.toJSONString(requestMap), UserGroup.class); - UserGroup lastMaxCodeGroup = mongoTemplate.findOne(new Query().with(Sort.by(Sort.Order.desc("groupCode"))), UserGroup.class); + // 默认是根组织 + String parentCode = "-1"; + if (userGroup.getParentCode() != null && !"".equals(userGroup.getParentCode())) { + parentCode = userGroup.getParentCode(); + } + UserGroup lastMaxCodeGroup = mongoTemplate + .findOne(new Query(Criteria.where("parentCode").is(parentCode)) + .with(Sort.by(Sort.Order.desc("groupCode"))).limit(1), UserGroup.class); if (lastMaxCodeGroup == null) { userGroup.setGroupCode("001"); userGroup.setParentCode("-1"); @@ -81,7 +88,6 @@ public class UserGroupServiceImpl implements IUserGroupService { } else { resultObj.put("error", "创建失败"); } - return resultObj; } diff --git a/src/main/java/com/objecteye/utils/GlobalUtil.java b/src/main/java/com/objecteye/utils/GlobalUtil.java index d8c2a86..156aba2 100644 --- a/src/main/java/com/objecteye/utils/GlobalUtil.java +++ b/src/main/java/com/objecteye/utils/GlobalUtil.java @@ -35,6 +35,10 @@ public class GlobalUtil { public final static String PREVIEWIPLISTLIST = "previewIpList"; public final static String ALARMMSG = "alarmMsg"; public final static String DEPLOYIDANDENDTIME = "deployIdAndTime"; + /** + * 已经登录的用户token相关信息, hashKey = loginUserToken, key: username, value: login time(long type) + */ + public final static String LOGIN_USER_TOKEN = "loginUserToken"; public final static String COMMON_HEADER_CONTENT_TYPE = "application/json;charset=UTF-8";