From 80e46c8cb8a28b9d1ca68d3d92b1b7b144cbfa7c Mon Sep 17 00:00:00 2001 From: Saturneric Date: Mon, 16 Mar 2020 12:41:15 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=AE=A4=E8=AF=81=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=9A=84API=E6=8E=A5=E5=8F=A3=E6=A0=BC=E5=BC=8F;?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BB=A3=E5=8F=B7?= =?UTF-8?q?,=20=E5=8F=96=E6=B6=88dataSHA1;=E8=B0=83=E6=95=B4Spring=20Secur?= =?UTF-8?q?ity;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/JSONRandomCodeGenerator.java | 5 +- .../auth/JSONTokenAuthenticationToken.java | 34 ++++--- ...enUsernamePasswordAuthenticationToken.java | 38 ++++++++ .../component/datamanager/JSONParameter.java | 36 ++++++++ .../json/request/UserLoginChecker.java | 5 +- .../json/respond/EmptyDataObjectRespond.java | 5 + .../json/respond/FailedSONRespond.java | 9 -- .../json/respond/JSONBaseRespondObject.java | 45 ++++++++- .../respond/JSONStandardFailedRespond.java | 8 ++ .../respond/UserLoginCheckerJSONRespond.java | 7 +- .../respond/UserLoginTokenJSONRespond.java | 2 +- .../ASEAuthenticationEntryPoint.java | 15 +-- .../ASEAuthenticationFailureHandler.java | 2 +- .../ASEAuthenticationSuccessHandler.java | 26 +++--- .../ASEJSONTokenAuthenticationFilter.java | 92 +++++++++---------- .../ASESecurityAuthenticationProvider.java | 17 +++- ...EUsernamePasswordAuthenticationFilter.java | 15 ++- .../ase/controller/LoginController.java | 11 +-- .../codesdream/ase/model/auth/JSONToken.java | 3 + .../codesdream/ase/service/AuthService.java | 8 +- .../codesdream/ase/service/IAuthService.java | 2 +- 21 files changed, 250 insertions(+), 135 deletions(-) create mode 100644 src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java create mode 100644 src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java delete mode 100644 src/main/java/com/codesdream/ase/component/json/respond/FailedSONRespond.java create mode 100644 src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java index 9b5dced..954850b 100644 --- a/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java +++ b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java @@ -6,13 +6,14 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Date; +// 随机特征码生成器 @Component public class JSONRandomCodeGenerator { @Resource private SHA1Encoder encoder; - public String generateRandomCode(String username, Date date, String apiSHA1){ + public String generateRandomCode(String username, Date date, String clientCode){ return encoder.encode(String.format("RandomCode [%s][%s][%s]", - username, date.toString(), apiSHA1)); + username, date.toString(), clientCode)); } } diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java index cafc05d..92bf5f9 100644 --- a/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java +++ b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java @@ -7,14 +7,15 @@ import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; -// 关联Token与其他用户的相关数据的认证对象 +// 关联Token与其他用户的相关数据的授权柄 public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken { - // token 产生的签名 - String signed = null; + // 客户端签名 + private String signed = null; // 用户名 - Object principal = null; - // JSON 特征随机代码 - String randomCode = null; + private Object principal = null; + // 客户端代码 + private String clientCode = null; + /** * Creates a token with the supplied array of authorities. @@ -22,18 +23,21 @@ public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken { * @param authorities the collection of GrantedAuthoritys for the principal * represented by this authentication object. */ - public JSONTokenAuthenticationToken(UserDetails principal, String signed, Collection authorities) { + public JSONTokenAuthenticationToken(UserDetails principal, + String clientCode, + Collection authorities) + { super(authorities); this.principal = principal; - this.randomCode = null; - this.signed = signed; + this.clientCode = clientCode; + this.signed = null; setAuthenticated(true); } - public JSONTokenAuthenticationToken(String principal, String randomCode, String signed) { + public JSONTokenAuthenticationToken(String principal, String clientCode, String signed) { super(null); this.principal = principal; - this.randomCode = randomCode; + this.clientCode = clientCode; this.signed = signed; setAuthenticated(false); } @@ -48,11 +52,11 @@ public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken { return principal; } - public String getRandomCode() { - return randomCode; + public String getClientCode() { + return clientCode; } - public void setRandomCode(String randomCode) { - this.randomCode = randomCode; + public void setClientCode(String clientCode) { + this.clientCode = clientCode; } } diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java b/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java new file mode 100644 index 0000000..14cc5fd --- /dev/null +++ b/src/main/java/com/codesdream/ase/component/auth/JSONTokenUsernamePasswordAuthenticationToken.java @@ -0,0 +1,38 @@ +package com.codesdream.ase.component.auth; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +// 明文用户名密码验证授权柄 +public class JSONTokenUsernamePasswordAuthenticationToken extends AbstractAuthenticationToken { + // 用户名 + private String username = null; + // 明文密码 + private String password = null; + // 授权柄 + private String clientCode = null; + + public JSONTokenUsernamePasswordAuthenticationToken(String username, String password, String clientCode) { + super(null); + this.username = username; + this.password = password; + this.clientCode = clientCode; + setAuthenticated(false); + } + + @Override + public Object getCredentials() { + return password; + } + + @Override + public Object getPrincipal() { + return username; + } + // 扩展接口 获得客户端代码 + public String getClientCode() { + return clientCode; + } +} diff --git a/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java b/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java index 726d4e4..103c5a0 100644 --- a/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java +++ b/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java @@ -2,9 +2,11 @@ package com.codesdream.ase.component.datamanager; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.codesdream.ase.component.json.respond.JSONBaseRespondObject; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; @@ -56,6 +58,38 @@ public class JSONParameter { return JSON.toJSONString(object); } + // 根据对象构造获得标准的JSON响应字符串返回 + public String getJSONStandardRespond(Integer status, String msg, Object dataObject){ + JSONBaseRespondObject respondObject = new JSONBaseRespondObject(status, msg); + respondObject.setData(dataObject); + return getJSONString(respondObject); + } + + // 获得标准的JSON响应字符串返回(404状态) + public String getJSONStandardRespond404(String msg){ + JSONBaseRespondObject respondObject = new JSONBaseRespondObject(404, msg); + return getJSONString(respondObject); + } + + // 获得标准的JSON响应字符串返回(500状态) + public String getJSONStandardRespond500(String msg){ + JSONBaseRespondObject respondObject = new JSONBaseRespondObject(500, msg); + return getJSONString(respondObject); + } + + // 获得标准的JSON响应字符串返回(200状态) + public String getJSONStandardRespond200(Object dataObject){ + JSONBaseRespondObject respondObject = new JSONBaseRespondObject(200, "ok"); + respondObject.setData(dataObject); + return getJSONString(respondObject); + } + + // 获得标准的JSON响应字符串返回(403状态) + public String getJSONStandardRespond403(){ + JSONBaseRespondObject respondObject = new JSONBaseRespondObject(403, "forbidden"); + return getJSONString(respondObject); + } + // 由JSON对象获得对应的Java对象 public T getJavaObject(JSONObject json, Class type){ return json.toJavaObject(type); @@ -67,4 +101,6 @@ public class JSONParameter { return json.map(jsonObject -> getJavaObject(jsonObject, type)); } + + } diff --git a/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java b/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java index 4f82267..0f706cf 100644 --- a/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java +++ b/src/main/java/com/codesdream/ase/component/json/request/UserLoginChecker.java @@ -8,9 +8,6 @@ public class UserLoginChecker { private String checkType; private String username; private String password; + private String clientCode; -// // 客户端类型 -// private String clientType; -// // JSON签名 -// private String signed; } diff --git a/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java new file mode 100644 index 0000000..e3ae905 --- /dev/null +++ b/src/main/java/com/codesdream/ase/component/json/respond/EmptyDataObjectRespond.java @@ -0,0 +1,5 @@ +package com.codesdream.ase.component.json.respond; + +public class EmptyDataObjectRespond { + +} diff --git a/src/main/java/com/codesdream/ase/component/json/respond/FailedSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/FailedSONRespond.java deleted file mode 100644 index e0c6de7..0000000 --- a/src/main/java/com/codesdream/ase/component/json/respond/FailedSONRespond.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.codesdream.ase.component.json.respond; - -// 请求失败返回JSON -public class FailedSONRespond extends JSONBaseRespondObject { - public FailedSONRespond(){ - super(); - this.status = "fail"; - } -} diff --git a/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java b/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java index dd624d9..f180023 100644 --- a/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java +++ b/src/main/java/com/codesdream/ase/component/json/respond/JSONBaseRespondObject.java @@ -2,20 +2,57 @@ package com.codesdream.ase.component.json.respond; import com.codesdream.ase.component.json.JSONBaseObject; import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.Date; // 服务端返回的JSON对象模板 +@EqualsAndHashCode(callSuper = true) @Data public class JSONBaseRespondObject extends JSONBaseObject { - // 请求成功状态 - String status = "fail"; - public JSONBaseRespondObject(){ + // 存放返回内容 + private Object data = new EmptyDataObjectRespond(); + // 存放响应信息提示 + private String msg = ""; + + // 状态 + private Integer status = 200; + + public JSONBaseRespondObject(String msg){ + super(); + this.status = 200; + this.msg = msg; } - public JSONBaseRespondObject(String status){ + public JSONBaseRespondObject(Integer status, String msg){ + super(); this.status = status; + this.msg = msg; + } + + public void setStatusNotFound(){ + this.status = 404; + } + + public void setStatusBadRequest(){ + this.status = 400; + } + + public void setStatusUnauthorized(){ + this.status = 401; + } + + public void setStatusForbidden(){ + this.status = 403; + } + + public void setStatusInternalServerError(){ + this.status = 500; + } + + public void setStatusOK(){ + this.status = 200; } } diff --git a/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java new file mode 100644 index 0000000..ba1626f --- /dev/null +++ b/src/main/java/com/codesdream/ase/component/json/respond/JSONStandardFailedRespond.java @@ -0,0 +1,8 @@ +package com.codesdream.ase.component.json.respond; + +// 请求失败返回JSON +public class JSONStandardFailedRespond extends JSONBaseRespondObject { + public JSONStandardFailedRespond(){ + super(500, "failed"); + } +} diff --git a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java index e4112ca..79ccfe9 100644 --- a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java +++ b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginCheckerJSONRespond.java @@ -3,17 +3,12 @@ package com.codesdream.ase.component.json.respond; import lombok.Data; import lombok.EqualsAndHashCode; -@EqualsAndHashCode(callSuper = true) @Data -public class UserLoginCheckerJSONRespond extends JSONBaseRespondObject { +public class UserLoginCheckerJSONRespond { boolean userExist = false; boolean loginStatus = false; boolean userBanned = false; String respondInformation = ""; String token = ""; - public UserLoginCheckerJSONRespond(){ - super("success"); - } - } diff --git a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java index 48b087e..43272ab 100644 --- a/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java +++ b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java @@ -1,6 +1,6 @@ package com.codesdream.ase.component.json.respond; // 返回登录后的授权码(用于客户端生成签名) -public class UserLoginTokenJSONRespond extends JSONBaseRespondObject { +public class UserLoginTokenJSONRespond { String token; } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java index caf783c..3e62a3f 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java @@ -1,6 +1,7 @@ package com.codesdream.ase.component.permission; import com.codesdream.ase.component.datamanager.JSONParameter; +import com.codesdream.ase.component.json.respond.JSONBaseRespondObject; import com.codesdream.ase.component.json.respond.UserLoginCheckerJSONRespond; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.AuthenticationException; @@ -23,18 +24,8 @@ public class ASEAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - log.info("ASEAuthenticationEntryPoint Found!"); - - response.setCharacterEncoding("utf-8"); - response.setContentType("text/javascript;charset=utf-8"); - UserLoginCheckerJSONRespond checkerRespond = new UserLoginCheckerJSONRespond(); - checkerRespond.setLoginStatus(false); - checkerRespond.setUserExist(false); - checkerRespond.setUserBanned(true); - checkerRespond.setRespondInformation("Anonymous user has no access to this resource"); - - // 对匿名用户返回 - response.getWriter().print(jsonParameter.getJSONString(checkerRespond)); + // 对匿名用户返回403 + response.getWriter().print(jsonParameter.getJSONStandardRespond403()); } } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java index 86ab588..1680ec3 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java @@ -33,6 +33,6 @@ public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFail respond.setRespondInformation("Authentication Failed"); // 填充response对象 - response.getWriter().write(jsonParameter.getJSONString(respond)); + response.getWriter().write(jsonParameter.getJSONStandardRespond200(respond)); } } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java index d8ea606..9abed5f 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java @@ -1,5 +1,6 @@ package com.codesdream.ase.component.permission; +import com.codesdream.ase.component.auth.JSONTokenAuthenticationToken; import com.codesdream.ase.component.datamanager.JSONParameter; import com.codesdream.ase.component.json.respond.UserLoginCheckerJSONRespond; import com.codesdream.ase.model.permission.User; @@ -35,31 +36,26 @@ public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthentica public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - // 对AJAX登录请求特殊化处理 -/* - if(Optional.ofNullable(request.getHeader("X-Requested-With")).isPresent()) { - HttpSession session = request.getSession(); - SecurityContext securityContext = SecurityContextHolder.getContext(); - - session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext); - } -*/ UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond(); respond.setUserExist(authentication.isAuthenticated()); respond.setLoginStatus(authentication.isAuthenticated()); + respond.setRespondInformation("Authentication Success"); + + // 获得 JSONTokenAuthenticationToken + JSONTokenAuthenticationToken authenticationToken = (JSONTokenAuthenticationToken) authentication; + + User user = (User) authenticationToken.getPrincipal(); + + Optional tokenOptional = authService.userNewTokenGetter( + user.getUsername(), authenticationToken.getClientCode()); - // 获得session id - /*WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) (authentication.getDetails());*/ - User user = (User) authentication.getPrincipal(); - // 获得api token - Optional tokenOptional = authService.userNewTokenGetter(user.getUsername()); if(tokenOptional.isPresent()){ respond.setToken(tokenOptional.get()); } else respond.setToken(""); - response.getWriter().write(jsonParameter.getJSONString(respond)); + response.getWriter().write(jsonParameter.getJSONStandardRespond200(respond)); } } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java b/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java index d22ec0d..a518944 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java @@ -1,29 +1,18 @@ package com.codesdream.ase.component.permission; -import com.alibaba.fastjson.JSONObject; import com.codesdream.ase.component.auth.AJAXRequestChecker; import com.codesdream.ase.component.auth.JSONRandomCodeGenerator; import com.codesdream.ase.component.auth.JSONSignedGenerator; import com.codesdream.ase.component.auth.JSONTokenAuthenticationToken; import com.codesdream.ase.component.datamanager.JSONParameter; -import com.codesdream.ase.exception.JSONTokenExpiredException; -import com.codesdream.ase.exception.JSONTokenIncorrectSignedException; import com.codesdream.ase.model.auth.JSONToken; import com.codesdream.ase.service.AuthService; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.annotation.Resource; @@ -32,7 +21,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.nio.file.attribute.UserPrincipalNotFoundException; import java.util.Collection; import java.util.Date; import java.util.Optional; @@ -64,56 +52,68 @@ public class ASEJSONTokenAuthenticationFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 用户名 String username = request.getHeader( "username"); + // 客户端签名 String signed = request.getHeader("signed"); - String rawDate = request.getHeader("date"); + // 时间戳 + String timestamp = request.getHeader("timestamp"); - // 对API的内容取哈希码 - String apiSHA1 = request.getHeader("apiSHA1"); - - if (signed != null && username != null && rawDate != null && apiSHA1 != null) { + if (signed != null && username != null && timestamp != null) { // 获得具体时间 - Date date = new Date(Long.parseLong(rawDate)); + Date date = new Date(Long.parseLong(timestamp)); - // 检查时间是否合理 - // ... + Date now = new Date(); - // 生成特征随机代码 - String randomCode = randomCodeGenerator.generateRandomCode(username, date, apiSHA1); + // 限制时间戳有效区间为60s + long dtTime = 60*1000; + Date maxDate = new Date(now.getTime() + dtTime); - // 进行验证 - Optional optionalJSONToken = authService.findTokenByUserName(username); - if(!optionalJSONToken.isPresent()){ - throw new UserPrincipalNotFoundException("Token Not Found"); - } + // 检查时间戳是否合理 + if(maxDate.after(date)) { + // 从服务器中查找token + Optional optionalJSONToken = authService.findTokenByUserName(username); + if (optionalJSONToken.isPresent()) { + JSONToken token = optionalJSONToken.get(); - // 检查token是否过期 - JSONToken token = optionalJSONToken.get(); - if(!authService.checkTokenIfExpired(token)) { + // 检查token是否过期 + if (!authService.checkTokenIfExpired(token)) { + // 生成特征随机代码 + String randomCode = randomCodeGenerator.generateRandomCode(username, date, token.getClientCode()); - log.info(String.format("Determined Signed: %s", - signedGenerator.generateSigned(username, randomCode, token.getToken()))); + log.info(String.format("Determined Signed: %s", + signedGenerator.generateSigned(username, randomCode, token.getToken()))); - // 检查签名是否正确 - if (signed.equals(signedGenerator.generateSigned(username, randomCode, token.getToken()))) { - - // 查询用户的相关信息 - UserDetails user = userDetailsService.loadUserByUsername(username); - - Collection authorities = user.getAuthorities(); - - // 生成认证柄 (储存上下文信息) - JSONTokenAuthenticationToken authentication = new JSONTokenAuthenticationToken(user, signed, authorities); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - SecurityContextHolder.getContext().setAuthentication(authentication); + // 检查签名是否正确 + if (signed.equals(signedGenerator.generateSigned(username, randomCode, token.getToken()))) { + // 执行授权操作 + doAuthentication(username, request); + } + } } } - } filterChain.doFilter(request, response); } + // 执行授权 + private void doAuthentication(String username, HttpServletRequest request){ + // 查询用户的相关信息 + UserDetails user = userDetailsService.loadUserByUsername(username); + + // 生成用户权限列表 + Collection authorities = user.getAuthorities(); + + // 生成授权柄 (储存上下文信息) + JSONTokenAuthenticationToken authentication = + new JSONTokenAuthenticationToken(user, null, authorities); + + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + // 执行授权 + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java b/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java index 9bf6a94..7ba70ca 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java @@ -1,5 +1,7 @@ package com.codesdream.ase.component.permission; +import com.codesdream.ase.component.auth.JSONTokenAuthenticationToken; +import com.codesdream.ase.component.auth.JSONTokenUsernamePasswordAuthenticationToken; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; @@ -29,10 +31,15 @@ public class ASESecurityAuthenticationProvider implements AuthenticationProvider @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { + JSONTokenUsernamePasswordAuthenticationToken authenticationToken = + (JSONTokenUsernamePasswordAuthenticationToken) authentication; + // 获得登录表单中的学号 - String username = usernameEncoder.encode(authentication.getName()); + String username = usernameEncoder.encode((CharSequence) authenticationToken.getPrincipal()); // 获得表单中的密码 - String password = passwordEncoder.encode(authentication.getCredentials().toString()); + String password = passwordEncoder.encode((CharSequence) authenticationToken.getCredentials()); + // 获得 + String clientCode = authenticationToken.getClientCode(); // 判断用户是否存在 UserDetails userInfo = userDetailsService.loadUserByUsername(username); @@ -58,12 +65,14 @@ public class ASESecurityAuthenticationProvider implements AuthenticationProvider throw new AccountExpiredException("User IS Expired"); } + // 生成权限列表 Collection authorities = userInfo.getAuthorities(); - return new UsernamePasswordAuthenticationToken(userInfo, password, authorities); + + return new JSONTokenAuthenticationToken(userInfo, clientCode, authorities); } @Override public boolean supports(Class aClass) { - return aClass.equals(UsernamePasswordAuthenticationToken.class); + return aClass.equals(JSONTokenUsernamePasswordAuthenticationToken.class); } } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java b/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java index 2868d42..2be84dd 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java @@ -1,6 +1,7 @@ package com.codesdream.ase.component.permission; import com.codesdream.ase.component.auth.AJAXRequestChecker; +import com.codesdream.ase.component.auth.JSONTokenUsernamePasswordAuthenticationToken; import com.codesdream.ase.component.datamanager.JSONParameter; import com.codesdream.ase.component.json.request.UserLoginChecker; import lombok.extern.slf4j.Slf4j; @@ -10,6 +11,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -32,19 +34,19 @@ public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAut // 判断是否为AJAX请求格式的数据 if(!ajaxRequestChecker.checkAjaxPOSTRequest(request)) { - log.info("NOT AJAX POST Request."); throw new AuthenticationServiceException("Authentication method not supported: NOT Ajax Method."); } Optional checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class); if(!checker.isPresent()) throw new BadCredentialsException("Invalid AJAX JSON Request"); - log.info("JSON Object 2 Java Object Success."); + if (!checker.get().getCheckType().equals("UsernamePasswordChecker")) throw new AuthenticationServiceException("Authentication not supported: NOT Username Password Type."); // 获得相应的用户名密码 String username = checker.get().getUsername(); String password = checker.get().getPassword(); + String clientCode = checker.get().getClientCode(); if (username == null) username = ""; if (password == null) password = ""; @@ -53,15 +55,12 @@ public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAut username = username.trim(); password = password.trim(); - UsernamePasswordAuthenticationToken authRequest = - new UsernamePasswordAuthenticationToken(username, password); + JSONTokenUsernamePasswordAuthenticationToken authRequest = + new JSONTokenUsernamePasswordAuthenticationToken(username, password, clientCode); - log.info(String.format("User AJAX JSON Authentication: %s %s.", username, password)); - - setDetails(request, authRequest); + authRequest.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); return this.getAuthenticationManager().authenticate(authRequest); - } } diff --git a/src/main/java/com/codesdream/ase/controller/LoginController.java b/src/main/java/com/codesdream/ase/controller/LoginController.java index 9796ea7..db99311 100644 --- a/src/main/java/com/codesdream/ase/controller/LoginController.java +++ b/src/main/java/com/codesdream/ase/controller/LoginController.java @@ -2,7 +2,7 @@ package com.codesdream.ase.controller; import com.alibaba.fastjson.JSONObject; import com.codesdream.ase.component.datamanager.JSONParameter; -import com.codesdream.ase.component.json.respond.FailedSONRespond; +import com.codesdream.ase.component.json.respond.JSONStandardFailedRespond; import com.codesdream.ase.component.json.respond.JSONBaseRespondObject; import com.codesdream.ase.component.permission.ASEUsernameEncoder; import com.codesdream.ase.component.json.request.UserLoginChecker; @@ -17,7 +17,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.security.Principal; import java.util.Optional; @@ -48,7 +47,7 @@ public class LoginController { // 检查是否为JSON Optional json = jsonParameter.getJSONByRequest(request); - if(!json.isPresent()) return jsonParameter.getJSONString(new FailedSONRespond()); + if(!json.isPresent()) return jsonParameter.getJSONString(new JSONStandardFailedRespond()); UserLoginChecker loginChecker = json.get().toJavaObject(UserLoginChecker.class); @@ -66,7 +65,7 @@ public class LoginController { } else { // 返回失败对象 - return jsonParameter.getJSONString(new JSONBaseRespondObject()); + return jsonParameter.getJSONString(new JSONStandardFailedRespond()); } } @@ -76,7 +75,7 @@ public class LoginController { String checkUsernameByStudentID(HttpServletRequest request){ // 检查是否为JSON Optional json = jsonParameter.getJSONByRequest(request); - if(!json.isPresent()) return jsonParameter.getJSONString(new FailedSONRespond()); + if(!json.isPresent()) return jsonParameter.getJSONString(new JSONStandardFailedRespond()); UserLoginChecker loginChecker = json.get().toJavaObject(UserLoginChecker.class); @@ -87,7 +86,7 @@ public class LoginController { } else { // 返回失败对象 - return jsonParameter.getJSONString(new JSONBaseRespondObject()); + return jsonParameter.getJSONString(new JSONStandardFailedRespond()); } diff --git a/src/main/java/com/codesdream/ase/model/auth/JSONToken.java b/src/main/java/com/codesdream/ase/model/auth/JSONToken.java index e88c51b..751ebf1 100644 --- a/src/main/java/com/codesdream/ase/model/auth/JSONToken.java +++ b/src/main/java/com/codesdream/ase/model/auth/JSONToken.java @@ -23,6 +23,9 @@ public class JSONToken { @Column(unique = true) private String token; + // 客户端标识口令 + private String clientCode; + // token过期时间 private Date expiredDate; } diff --git a/src/main/java/com/codesdream/ase/service/AuthService.java b/src/main/java/com/codesdream/ase/service/AuthService.java index f88aff7..1784365 100644 --- a/src/main/java/com/codesdream/ase/service/AuthService.java +++ b/src/main/java/com/codesdream/ase/service/AuthService.java @@ -36,18 +36,24 @@ public class AuthService implements IAuthService { } @Override - public Optional userNewTokenGetter(String username) { + public Optional userNewTokenGetter(String username, String clientCode) { Pair userPair = userService.checkIfUserExists(username); if(userPair.getKey()){ Optional jsonTokenOptional = jsonTokenRepository.findByUsername(username); JSONToken token = jsonTokenOptional.orElseGet(JSONToken::new); + // 过期时间设置为三十分钟后 long currentTime = System.currentTimeMillis(); currentTime +=30*60*1000; token.setExpiredDate(new Date(currentTime)); token.setToken(authTokenGenerator.generateAuthToken(username)); + + // 设置用户名 token.setUsername(username); + // 设置客户端代号 + token.setClientCode(clientCode); + // 在数据库中更新新的token token = jsonTokenRepository.save(token); return Optional.ofNullable(token.getToken()); diff --git a/src/main/java/com/codesdream/ase/service/IAuthService.java b/src/main/java/com/codesdream/ase/service/IAuthService.java index a3f6a7b..fc4eae0 100644 --- a/src/main/java/com/codesdream/ase/service/IAuthService.java +++ b/src/main/java/com/codesdream/ase/service/IAuthService.java @@ -12,5 +12,5 @@ public interface IAuthService { boolean checkTokenIfExpired(JSONToken token); // 为用户获得一个新的API Token - Optional userNewTokenGetter(String username); + Optional userNewTokenGetter(String username, String clientCode); }