Commit 00762fb4f483df1c1c9d5fa9abf3116efe84b3ab
1 parent
68a67f36
代码重构;
token添加过期功能; 添加只能一点登录功能;
Showing
14 changed files
with
103 additions
and
23 deletions
src/main/java/com/objecteye/config/AuthenticationHeadFilter.java
1 | 1 | package com.objecteye.config; |
2 | 2 | |
3 | 3 | import com.alibaba.fastjson.JSON; |
4 | +import com.objecteye.pojo.AuthenticationToken; | |
4 | 5 | import com.objecteye.pojo.TokenUser; |
6 | +import com.objecteye.utils.GlobalUtil; | |
7 | +import org.springframework.data.redis.core.RedisTemplate; | |
5 | 8 | import org.springframework.security.core.context.SecurityContextHolder; |
6 | 9 | import org.springframework.security.jwt.Jwt; |
7 | 10 | import org.springframework.security.jwt.JwtHelper; |
... | ... | @@ -22,6 +25,12 @@ public class AuthenticationHeadFilter extends OncePerRequestFilter { |
22 | 25 | |
23 | 26 | private RsaVerifier rsaVerifier; |
24 | 27 | |
28 | + private RedisTemplate redisTemplate; | |
29 | + | |
30 | + public void setRedisTemplate(RedisTemplate redisTemplate) { | |
31 | + this.redisTemplate = redisTemplate; | |
32 | + } | |
33 | + | |
25 | 34 | public void setRsaVerifier(RsaVerifier rsaVerifier) { |
26 | 35 | this.rsaVerifier = rsaVerifier; |
27 | 36 | } |
... | ... | @@ -33,12 +42,25 @@ public class AuthenticationHeadFilter extends OncePerRequestFilter { |
33 | 42 | filterChain.doFilter(httpServletRequest, httpServletResponse); |
34 | 43 | return; |
35 | 44 | } |
36 | - | |
37 | 45 | TokenUser user; |
38 | 46 | try { |
39 | 47 | Jwt jwt = JwtHelper.decodeAndVerify(token, rsaVerifier); |
40 | 48 | String claims = jwt.getClaims(); |
41 | 49 | user = JSON.parseObject(claims, TokenUser.class); |
50 | + // 是否单点登录 | |
51 | + Long createTime = user.getCreateTime(); | |
52 | + Long tokenCreateTime = (Long) redisTemplate.opsForHash().get(GlobalUtil.LOGIN_USER_TOKEN, user.getUsername()); | |
53 | + if (!createTime.equals(tokenCreateTime)) { | |
54 | + httpServletResponse.setContentType("application/json;charset=UTF-8"); | |
55 | + httpServletResponse.getWriter().write("用户已在其他地方登录"); | |
56 | + return; | |
57 | + } | |
58 | + // 是否超时(默认1小时) | |
59 | + if (System.currentTimeMillis() > createTime + 1000 * 60 * 60) { | |
60 | + httpServletResponse.setContentType("application/json;charset=UTF-8"); | |
61 | + httpServletResponse.getWriter().write("Token已过期请重新登录"); | |
62 | + return; | |
63 | + } | |
42 | 64 | } catch (Exception e) { |
43 | 65 | e.printStackTrace(); |
44 | 66 | httpServletResponse.setContentType("application/json;charset=UTF-8"); | ... | ... |
src/main/java/com/objecteye/config/AuthenticationLoginFilter.java
1 | 1 | package com.objecteye.config; |
2 | 2 | |
3 | +import com.objecteye.pojo.AuthenticationToken; | |
3 | 4 | import org.springframework.security.core.Authentication; |
4 | 5 | import org.springframework.security.core.AuthenticationException; |
5 | 6 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; | ... | ... |
src/main/java/com/objecteye/config/AuthenticationProviderConfig.java
src/main/java/com/objecteye/config/WebSecurityConfig.java
1 | 1 | package com.objecteye.config; |
2 | 2 | |
3 | 3 | import com.objecteye.common.ResultCode; |
4 | +import com.objecteye.handle.LoginFailureHandler; | |
5 | +import com.objecteye.handle.LoginSuccessHandler; | |
4 | 6 | import com.objecteye.service.impl.UserDetailServiceImpl; |
5 | 7 | import org.springframework.beans.factory.annotation.Autowired; |
6 | 8 | import org.springframework.context.annotation.Bean; |
7 | 9 | import org.springframework.context.annotation.Configuration; |
10 | +import org.springframework.data.redis.core.RedisTemplate; | |
8 | 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
9 | 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
10 | 13 | import org.springframework.security.config.http.SessionCreationPolicy; |
... | ... | @@ -26,6 +29,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
26 | 29 | @Autowired |
27 | 30 | private UserDetailServiceImpl userDetailService; |
28 | 31 | |
32 | + @Autowired | |
33 | + private RedisTemplate redisTemplate; | |
34 | + | |
29 | 35 | @Override |
30 | 36 | protected void configure(HttpSecurity http) throws Exception { |
31 | 37 | AuthenticationLoginFilter authenticationLoginFilter = new AuthenticationLoginFilter(); |
... | ... | @@ -33,6 +39,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
33 | 39 | |
34 | 40 | LoginSuccessHandler loginSuccessHandler = new LoginSuccessHandler(); |
35 | 41 | loginSuccessHandler.setSigner(signer); |
42 | + loginSuccessHandler.setRedisTemplate(redisTemplate); | |
36 | 43 | authenticationLoginFilter.setAuthenticationSuccessHandler(loginSuccessHandler); |
37 | 44 | authenticationLoginFilter.setAuthenticationFailureHandler(new LoginFailureHandler()); |
38 | 45 | |
... | ... | @@ -42,6 +49,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { |
42 | 49 | |
43 | 50 | AuthenticationHeadFilter headFilter = new AuthenticationHeadFilter(); |
44 | 51 | headFilter.setRsaVerifier(verifier); |
52 | + headFilter.setRedisTemplate(redisTemplate); | |
45 | 53 | |
46 | 54 | http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { |
47 | 55 | response.setContentType("application/json;charset=UTF-8"); | ... | ... |
src/main/java/com/objecteye/controller/UserController.java
... | ... | @@ -4,6 +4,7 @@ import com.objecteye.common.CommonResult; |
4 | 4 | import com.objecteye.service.UserServices; |
5 | 5 | import com.objecteye.utils.GlobalUtil; |
6 | 6 | import org.springframework.beans.factory.annotation.Autowired; |
7 | +import org.springframework.security.core.context.SecurityContextHolder; | |
7 | 8 | import org.springframework.web.bind.annotation.*; |
8 | 9 | |
9 | 10 | import java.util.Map; |
... | ... | @@ -16,6 +17,11 @@ public class UserController extends BasicController { |
16 | 17 | @Autowired |
17 | 18 | public UserServices userServices; |
18 | 19 | |
20 | + @RequestMapping(value = "whoami", method = RequestMethod.POST, produces = GlobalUtil.COMMON_HEADER_CONTENT_TYPE) | |
21 | + public CommonResult whoAmI() { | |
22 | + return CommonResult.success(SecurityContextHolder.getContext().getAuthentication().getPrincipal()); | |
23 | + } | |
24 | + | |
19 | 25 | @RequestMapping(value = "checkUser", method = RequestMethod.POST, produces = GlobalUtil.COMMON_HEADER_CONTENT_TYPE) |
20 | 26 | public CommonResult checkUser(@RequestBody Map<String, Object> requestMap) { |
21 | 27 | return jsonObjectResultHandle(userServices.checkUser(requestMap)); | ... | ... |
src/main/java/com/objecteye/config/LoginFailureHandler.java renamed to src/main/java/com/objecteye/handle/LoginFailureHandler.java
src/main/java/com/objecteye/config/LoginSuccessHandler.java renamed to src/main/java/com/objecteye/handle/LoginSuccessHandler.java
1 | -package com.objecteye.config; | |
1 | +package com.objecteye.handle; | |
2 | 2 | |
3 | 3 | import com.alibaba.fastjson.JSON; |
4 | +import com.alibaba.fastjson.JSONObject; | |
5 | +import com.objecteye.utils.GlobalUtil; | |
6 | +import org.springframework.data.redis.core.RedisTemplate; | |
4 | 7 | import org.springframework.security.core.Authentication; |
5 | 8 | import org.springframework.security.jwt.JwtHelper; |
6 | 9 | import org.springframework.security.jwt.crypto.sign.RsaSigner; |
... | ... | @@ -17,10 +20,15 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { |
17 | 20 | |
18 | 21 | private RsaSigner signer; |
19 | 22 | |
23 | + private RedisTemplate redisTemplate; | |
24 | + | |
20 | 25 | @Override |
21 | 26 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { |
22 | 27 | response.setContentType("application/json;charset=UTF-8"); |
23 | 28 | String userJsonStr = JSON.toJSONString(authentication.getPrincipal()); |
29 | + JSONObject userJson = JSON.parseObject(userJsonStr); | |
30 | + // 保存登录存根 | |
31 | + redisTemplate.opsForHash().put(GlobalUtil.LOGIN_USER_TOKEN, userJson.getString("username"), userJson.getLongValue("createTime")); | |
24 | 32 | String token = JwtHelper.encode(userJsonStr, signer).getEncoded(); |
25 | 33 | //签发token |
26 | 34 | response.getWriter().write("token=" + token); |
... | ... | @@ -29,4 +37,8 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { |
29 | 37 | public void setSigner(RsaSigner signer) { |
30 | 38 | this.signer = signer; |
31 | 39 | } |
40 | + | |
41 | + public void setRedisTemplate(RedisTemplate redisTemplate) { | |
42 | + this.redisTemplate = redisTemplate; | |
43 | + } | |
32 | 44 | } | ... | ... |
src/main/java/com/objecteye/config/AuthenticationToken.java renamed to src/main/java/com/objecteye/pojo/AuthenticationToken.java
src/main/java/com/objecteye/pojo/TokenUser.java
... | ... | @@ -17,11 +17,14 @@ public class TokenUser implements UserDetails { |
17 | 17 | |
18 | 18 | private String password; |
19 | 19 | |
20 | + private Long createTime; | |
21 | + | |
20 | 22 | private List<SimpleGrantedAuthority> simpleGrantedAuthorities; |
21 | 23 | |
22 | 24 | public TokenUser(String userName, String password, String... roles) { |
23 | 25 | this.userName = userName; |
24 | 26 | this.password = password; |
27 | + this.createTime = System.currentTimeMillis(); | |
25 | 28 | this.simpleGrantedAuthorities = Arrays.stream(roles).map(SimpleGrantedAuthority::new).collect(Collectors.toList()); |
26 | 29 | } |
27 | 30 | |
... | ... | @@ -74,4 +77,12 @@ public class TokenUser implements UserDetails { |
74 | 77 | public void setSimpleGrantedAuthorities(List<SimpleGrantedAuthority> simpleGrantedAuthorities) { |
75 | 78 | this.simpleGrantedAuthorities = simpleGrantedAuthorities; |
76 | 79 | } |
80 | + | |
81 | + public Long getCreateTime() { | |
82 | + return createTime; | |
83 | + } | |
84 | + | |
85 | + public void setCreateTime(Long createTime) { | |
86 | + this.createTime = createTime; | |
87 | + } | |
77 | 88 | } | ... | ... |
src/main/java/com/objecteye/pojo/UserGroup.java
src/main/java/com/objecteye/config/AccessConfirmServiceImpl.java renamed to src/main/java/com/objecteye/service/impl/AccessConfirmServiceImpl.java
1 | -package com.objecteye.config; | |
1 | +package com.objecteye.service.impl; | |
2 | 2 | |
3 | 3 | import com.objecteye.pojo.SpecialAuthenticationUrlConfig; |
4 | 4 | import com.objecteye.pojo.UserDetailsMsg; |
... | ... | @@ -28,6 +28,16 @@ public class AccessConfirmServiceImpl { |
28 | 28 | private AntPathMatcher antPathMatcher = new AntPathMatcher(); |
29 | 29 | |
30 | 30 | public boolean hasPermission(HttpServletRequest request, Authentication auth) { |
31 | + // 不需要权限的接口 | |
32 | + List<String> permitAll = new ArrayList<>(); | |
33 | + permitAll.add("/login"); | |
34 | + permitAll.add("/vehicle/user/addUser"); | |
35 | + | |
36 | + String requestUri = request.getRequestURI(); | |
37 | + if (permitAll.contains(requestUri)) { | |
38 | + return true; | |
39 | + } | |
40 | + | |
31 | 41 | // 匿名token不允许访问所有的接口 |
32 | 42 | if (auth instanceof AnonymousAuthenticationToken) { |
33 | 43 | return false; |
... | ... | @@ -36,7 +46,7 @@ public class AccessConfirmServiceImpl { |
36 | 46 | UserDetails user = (UserDetails) auth.getPrincipal(); |
37 | 47 | Map<String, Boolean> specialUrlAccessMap = queryUrlByUserName(user.getUsername()); |
38 | 48 | for (Map.Entry<String, Boolean> entry : specialUrlAccessMap.entrySet()) { |
39 | - if (antPathMatcher.match(entry.getKey(), request.getRequestURI())) { | |
49 | + if (antPathMatcher.match(entry.getKey(), requestUri)) { | |
40 | 50 | return entry.getValue(); |
41 | 51 | } |
42 | 52 | } | ... | ... |
src/main/java/com/objecteye/service/impl/UserDetailServiceImpl.java
... | ... | @@ -2,6 +2,7 @@ package com.objecteye.service.impl; |
2 | 2 | |
3 | 3 | import com.objecteye.pojo.TokenUser; |
4 | 4 | import com.objecteye.pojo.UserDetailsMsg; |
5 | +import com.objecteye.pojo.UserGroup; | |
5 | 6 | import org.springframework.beans.factory.annotation.Autowired; |
6 | 7 | import org.springframework.data.mongodb.core.MongoTemplate; |
7 | 8 | import org.springframework.data.mongodb.core.query.Criteria; |
... | ... | @@ -12,8 +13,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; |
12 | 13 | import org.springframework.security.crypto.password.PasswordEncoder; |
13 | 14 | import org.springframework.stereotype.Component; |
14 | 15 | |
15 | -import java.util.List; | |
16 | - | |
17 | 16 | @Component |
18 | 17 | public class UserDetailServiceImpl implements UserDetailsService { |
19 | 18 | |
... | ... | @@ -29,19 +28,19 @@ public class UserDetailServiceImpl implements UserDetailsService { |
29 | 28 | |
30 | 29 | @Override |
31 | 30 | public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { |
32 | - List<UserDetailsMsg> userDetailsMsgs = mongoTemplate.find(Query.query(Criteria.where("userName").is(s)), UserDetailsMsg.class); | |
33 | - | |
34 | - // admin | |
35 | - UserDetailsMsg superAdminUser = new UserDetailsMsg(); | |
36 | - superAdminUser.setUsername("superAdmin"); | |
37 | - superAdminUser.setPassword("123456"); | |
38 | - superAdminUser.setUserRole("1"); | |
39 | - userDetailsMsgs.add(superAdminUser); | |
40 | - if (userDetailsMsgs.size() > 0) { | |
41 | - UserDetailsMsg userDetailsMsg = userDetailsMsgs.get(0); | |
42 | - return new TokenUser(userDetailsMsg.getUsername(), passwordEncoder.encode(userDetailsMsg.getPassword())); | |
31 | + if ("superAdmin".equals(s)) { | |
32 | + return new TokenUser("superAdmin", passwordEncoder.encode("123456"), "1"); | |
33 | + } | |
34 | + UserDetailsMsg userDetailsMsg = mongoTemplate.findOne(Query.query(Criteria.where("username").is(s)), UserDetailsMsg.class); | |
35 | + if (userDetailsMsg != null) { | |
36 | + UserGroup userGroup = mongoTemplate.findOne(Query.query(Criteria.where("groupCode").is(userDetailsMsg.getGroup())), UserGroup.class); | |
37 | + String userRole = userDetailsMsg.getUserRole(); | |
38 | + if (userGroup != null) { | |
39 | + String groupLevel = userGroup.getGroupLevel(); | |
40 | + userRole = String.valueOf(Math.min(Integer.parseInt(userRole), Integer.parseInt(groupLevel))); | |
41 | + } | |
42 | + return new TokenUser(userDetailsMsg.getUsername(), passwordEncoder.encode(userDetailsMsg.getPassword()), userRole); | |
43 | 43 | } |
44 | - | |
45 | 44 | return null; |
46 | 45 | } |
47 | 46 | } | ... | ... |
src/main/java/com/objecteye/service/impl/UserGroupServiceImpl.java
... | ... | @@ -66,7 +66,14 @@ public class UserGroupServiceImpl implements IUserGroupService { |
66 | 66 | @Override |
67 | 67 | public JSONObject addGroup(Map<String, Object> requestMap) { |
68 | 68 | UserGroup userGroup = JSON.parseObject(JSON.toJSONString(requestMap), UserGroup.class); |
69 | - UserGroup lastMaxCodeGroup = mongoTemplate.findOne(new Query().with(Sort.by(Sort.Order.desc("groupCode"))), UserGroup.class); | |
69 | + // 默认是根组织 | |
70 | + String parentCode = "-1"; | |
71 | + if (userGroup.getParentCode() != null && !"".equals(userGroup.getParentCode())) { | |
72 | + parentCode = userGroup.getParentCode(); | |
73 | + } | |
74 | + UserGroup lastMaxCodeGroup = mongoTemplate | |
75 | + .findOne(new Query(Criteria.where("parentCode").is(parentCode)) | |
76 | + .with(Sort.by(Sort.Order.desc("groupCode"))).limit(1), UserGroup.class); | |
70 | 77 | if (lastMaxCodeGroup == null) { |
71 | 78 | userGroup.setGroupCode("001"); |
72 | 79 | userGroup.setParentCode("-1"); |
... | ... | @@ -81,7 +88,6 @@ public class UserGroupServiceImpl implements IUserGroupService { |
81 | 88 | } else { |
82 | 89 | resultObj.put("error", "创建失败"); |
83 | 90 | } |
84 | - | |
85 | 91 | return resultObj; |
86 | 92 | } |
87 | 93 | ... | ... |
src/main/java/com/objecteye/utils/GlobalUtil.java
... | ... | @@ -35,6 +35,10 @@ public class GlobalUtil { |
35 | 35 | public final static String PREVIEWIPLISTLIST = "previewIpList"; |
36 | 36 | public final static String ALARMMSG = "alarmMsg"; |
37 | 37 | public final static String DEPLOYIDANDENDTIME = "deployIdAndTime"; |
38 | + /** | |
39 | + * 已经登录的用户token相关信息, hashKey = loginUserToken, key: username, value: login time(long type) | |
40 | + */ | |
41 | + public final static String LOGIN_USER_TOKEN = "loginUserToken"; | |
38 | 42 | |
39 | 43 | public final static String COMMON_HEADER_CONTENT_TYPE = "application/json;charset=UTF-8"; |
40 | 44 | ... | ... |