diff --git a/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java b/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java
new file mode 100644
index 0000000..77c9bed
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/AJAXRequestChecker.java
@@ -0,0 +1,14 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Optional;
+
+@Component
+public class AJAXRequestChecker {
+ public boolean checkAjaxPOSTRequest(HttpServletRequest request){
+ return Optional.ofNullable(request.getHeader("X-Requested-With")).isPresent();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java b/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java
new file mode 100644
index 0000000..8977670
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/AuthTokenGenerator.java
@@ -0,0 +1,19 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.UUID;
+
+@Component
+public class AuthTokenGenerator {
+ @Resource
+ private SHA1Encoder encoder;
+
+ public String generateAuthToken(String username){
+ Date dateNow = new Date();
+ UUID uuid = UUID.randomUUID();
+ return encoder.encode(String.format("Token [%s][%d][%s]",username,dateNow.getTime(), uuid.toString()));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java
new file mode 100644
index 0000000..9b5dced
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONRandomCodeGenerator.java
@@ -0,0 +1,18 @@
+package com.codesdream.ase.component.auth;
+
+import com.alibaba.fastjson.JSONObject;
+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){
+ return encoder.encode(String.format("RandomCode [%s][%s][%s]",
+ username, date.toString(), apiSHA1));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java b/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java
new file mode 100644
index 0000000..b67a76c
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONSignedGenerator.java
@@ -0,0 +1,16 @@
+package com.codesdream.ase.component.auth;
+
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+// 用来给JSON生成签名
+@Component
+public class JSONSignedGenerator {
+ @Resource
+ SHA1Encoder encoder;
+
+ public String generateSigned(String username, String randomCode, String token){
+ return encoder.encode(String.format("SIGN [%s][%s][%s]",username, randomCode, token));
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java
new file mode 100644
index 0000000..cafc05d
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/JSONTokenAuthenticationToken.java
@@ -0,0 +1,58 @@
+package com.codesdream.ase.component.auth;
+
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+// 关联Token与其他用户的相关数据的认证对象
+public class JSONTokenAuthenticationToken extends AbstractAuthenticationToken {
+ // token 产生的签名
+ String signed = null;
+ // 用户名
+ Object principal = null;
+ // JSON 特征随机代码
+ String randomCode = null;
+
+ /**
+ * Creates a token with the supplied array of authorities.
+ *
+ * @param authorities the collection of GrantedAuthoritys for the principal
+ * represented by this authentication object.
+ */
+ public JSONTokenAuthenticationToken(UserDetails principal, String signed, Collection extends GrantedAuthority> authorities) {
+ super(authorities);
+ this.principal = principal;
+ this.randomCode = null;
+ this.signed = signed;
+ setAuthenticated(true);
+ }
+
+ public JSONTokenAuthenticationToken(String principal, String randomCode, String signed) {
+ super(null);
+ this.principal = principal;
+ this.randomCode = randomCode;
+ this.signed = signed;
+ setAuthenticated(false);
+ }
+
+ @Override
+ public String getCredentials() {
+ return signed;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return principal;
+ }
+
+ public String getRandomCode() {
+ return randomCode;
+ }
+
+ public void setRandomCode(String randomCode) {
+ this.randomCode = randomCode;
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java b/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java
new file mode 100644
index 0000000..e3102d2
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/auth/SHA1Encoder.java
@@ -0,0 +1,17 @@
+package com.codesdream.ase.component.auth;
+
+import org.apache.commons.codec.cli.Digest;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.stereotype.Component;
+
+// SHA1算法不可逆加密 主要用于JSON签名
+@Component
+public class SHA1Encoder {
+ String encode(CharSequence charSequence){
+ return DigestUtils.sha1Hex(charSequence.toString());
+ }
+
+ boolean match(CharSequence charSequence, String s){
+ return s.equals(encode(charSequence));
+ }
+}
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 97b932c..726d4e4 100644
--- a/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java
+++ b/src/main/java/com/codesdream/ase/component/datamanager/JSONParameter.java
@@ -19,7 +19,13 @@ public class JSONParameter {
// 处理Request Body
public String getRequestBody(HttpServletRequest request){
try {
- return request.getParameter("json");
+ StringBuilder stringBuilder = new StringBuilder();
+ BufferedReader reader = request.getReader();
+ reader.reset();
+ String line;
+ while ((line = reader.readLine()) != null)
+ stringBuilder.append(line);
+ return stringBuilder.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
diff --git a/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java b/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java
new file mode 100644
index 0000000..86504b4
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/JSONBaseObject.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.component.json;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+
+// 所有有效的JSON对象模板
+@Slf4j
+public class JSONBaseObject {
+ Date time = new Date();
+
+}
diff --git a/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java b/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java
new file mode 100644
index 0000000..910ee53
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/request/JSONBaseRequestObject.java
@@ -0,0 +1,13 @@
+package com.codesdream.ase.component.json.request;
+
+import com.codesdream.ase.component.json.JSONBaseObject;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+
+// 客户端请求的JSON模板
+@Slf4j
+public class JSONBaseRequestObject extends JSONBaseObject {
+ String signed;
+
+}
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 ac0c738..4f82267 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
@@ -4,7 +4,13 @@ import lombok.Data;
@Data
public class UserLoginChecker {
+ // 请求类型
private String checkType;
private String username;
private String password;
+
+// // 客户端类型
+// private String clientType;
+// // JSON签名
+// private String signed;
}
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 a441bd5..dd624d9 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
@@ -1,16 +1,15 @@
package com.codesdream.ase.component.json.respond;
+import com.codesdream.ase.component.json.JSONBaseObject;
import lombok.Data;
import java.util.Date;
-// 服务端返回的JSON对象基础信息
+// 服务端返回的JSON对象模板
@Data
-public class JSONBaseRespondObject {
+public class JSONBaseRespondObject extends JSONBaseObject {
// 请求成功状态
String status = "fail";
- // 时间
- Date time = new Date();
public JSONBaseRespondObject(){
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 bbd291c..e4112ca 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
@@ -10,7 +10,7 @@ public class UserLoginCheckerJSONRespond extends JSONBaseRespondObject {
boolean loginStatus = false;
boolean userBanned = false;
String respondInformation = "";
- String sessionId = "";
+ 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
new file mode 100644
index 0000000..48b087e
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/json/respond/UserLoginTokenJSONRespond.java
@@ -0,0 +1,6 @@
+package com.codesdream.ase.component.json.respond;
+
+// 返回登录后的授权码(用于客户端生成签名)
+public class UserLoginTokenJSONRespond extends JSONBaseRespondObject {
+ String token;
+}
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 cf200cb..86ab588 100644
--- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java
+++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java
@@ -25,7 +25,7 @@ public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFail
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException
{
- log.info("ASEAuthenticationSuccessHandler Login Fail!");
+ log.info("ASEAuthenticationFailureHandler Login Fail!");
UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond();
respond.setUserExist(false);
respond.setLoginStatus(false);
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 85543fd..d8ea606 100644
--- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java
+++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java
@@ -3,12 +3,14 @@ package com.codesdream.ase.component.permission;
import com.codesdream.ase.component.datamanager.JSONParameter;
import com.codesdream.ase.component.json.respond.UserLoginCheckerJSONRespond;
import com.codesdream.ase.model.permission.User;
+
+import com.codesdream.ase.service.IAuthService;
import lombok.extern.slf4j.Slf4j;
+
import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
+
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
-import org.springframework.security.web.authentication.WebAuthenticationDetails;
+
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@@ -26,29 +28,36 @@ public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthentica
@Resource
private JSONParameter jsonParameter;
+ @Resource
+ private IAuthService authService;
+
@Override
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);
}
-
- // 打印用户登录成功日志
- log.info(String.format("ASEAuthenticationSuccessHandler: %s Login Success.",
- ((User)authentication.getDetails()).getUsername()));
+*/
UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond();
respond.setUserExist(authentication.isAuthenticated());
respond.setLoginStatus(authentication.isAuthenticated());
// 获得session id
- WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) (authentication.getDetails());
- respond.setSessionId(webAuthenticationDetails.getSessionId());
+ /*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));
diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java b/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java
new file mode 100644
index 0000000..d22ec0d
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/component/permission/ASEJSONTokenAuthenticationFilter.java
@@ -0,0 +1,119 @@
+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;
+import javax.servlet.FilterChain;
+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;
+
+
+// API请求验证过滤
+@Slf4j
+public class ASEJSONTokenAuthenticationFilter extends OncePerRequestFilter {
+
+ @Resource
+ private JSONParameter jsonParameter;
+
+ @Resource
+ private JSONRandomCodeGenerator randomCodeGenerator;
+
+ @Resource
+ private AJAXRequestChecker ajaxRequestChecker;
+
+ @Resource
+ private AuthService authService;
+
+ @Resource
+ private JSONSignedGenerator signedGenerator;
+
+ @Resource
+ private UserDetailsService userDetailsService;
+
+ @Override
+ 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");
+
+ // 对API的内容取哈希码
+ String apiSHA1 = request.getHeader("apiSHA1");
+
+ if (signed != null && username != null && rawDate != null && apiSHA1 != null) {
+ // 获得具体时间
+ Date date = new Date(Long.parseLong(rawDate));
+
+ // 检查时间是否合理
+ // ...
+
+ // 生成特征随机代码
+ String randomCode = randomCodeGenerator.generateRandomCode(username, date, apiSHA1);
+
+ // 进行验证
+ Optional optionalJSONToken = authService.findTokenByUserName(username);
+ if(!optionalJSONToken.isPresent()){
+ throw new UserPrincipalNotFoundException("Token Not Found");
+ }
+
+ // 检查token是否过期
+ JSONToken token = optionalJSONToken.get();
+ if(!authService.checkTokenIfExpired(token)) {
+
+ 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 extends GrantedAuthority> authorities = user.getAuthorities();
+
+ // 生成认证柄 (储存上下文信息)
+ JSONTokenAuthenticationToken authentication = new JSONTokenAuthenticationToken(user, signed, authorities);
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ }
+
+ }
+
+ filterChain.doFilter(request, response);
+
+ }
+
+}
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 8e9dcf3..9bf6a94 100644
--- a/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java
+++ b/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collection;
+// 普通用户名密码验证, 用户获得Token
@Slf4j
@Component
public class ASESecurityAuthenticationProvider implements AuthenticationProvider {
@@ -63,6 +64,6 @@ public class ASESecurityAuthenticationProvider implements AuthenticationProvider
@Override
public boolean supports(Class> aClass) {
- return true;
+ return aClass.equals(UsernamePasswordAuthenticationToken.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 a9e71c1..2868d42 100644
--- a/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java
+++ b/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java
@@ -1,5 +1,6 @@
package com.codesdream.ase.component.permission;
+import com.codesdream.ase.component.auth.AJAXRequestChecker;
import com.codesdream.ase.component.datamanager.JSONParameter;
import com.codesdream.ase.component.json.request.UserLoginChecker;
import lombok.extern.slf4j.Slf4j;
@@ -22,41 +23,45 @@ public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAut
@Resource
private JSONParameter jsonParameter;
+ @Resource
+ private AJAXRequestChecker ajaxRequestChecker;
+
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// 判断是否为AJAX请求格式的数据
- if(Optional.ofNullable(request.getHeader("X-Requested-With")).isPresent()) {
-
- Optional checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class);
- if(!checker.isPresent()) throw new BadCredentialsException("Invalid AJAX JSON Request");
- if (!checker.get().getCheckType().equals("From"))
- throw new AuthenticationServiceException("Invalid Checker Type");
-
- // 获得相应的用户名密码
- String username = checker.get().getUsername();
- String password = checker.get().getPassword();
-
- if (username == null) username = "";
- if (password == null) password = "";
-
- // 去除首尾两端的空白字符
- username = username.trim();
- password = password.trim();
-
- UsernamePasswordAuthenticationToken authRequest =
- new UsernamePasswordAuthenticationToken(username, password);
-
-
- log.info(String.format("User AJAX JSON Authentication: %s %s.", username, password));
-
- setDetails(request, authRequest);
-
- return this.getAuthenticationManager().authenticate(authRequest);
- }
- else{
- return super.attemptAuthentication(request, response);
+ 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();
+
+ if (username == null) username = "";
+ if (password == null) password = "";
+
+ // 去除首尾两端的空白字符
+ username = username.trim();
+ password = password.trim();
+
+ UsernamePasswordAuthenticationToken authRequest =
+ new UsernamePasswordAuthenticationToken(username, password);
+
+
+ log.info(String.format("User AJAX JSON Authentication: %s %s.", username, password));
+
+ setDetails(request, authRequest);
+
+ return this.getAuthenticationManager().authenticate(authRequest);
+
}
}
diff --git a/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java
index 9659196..1bb0f98 100644
--- a/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java
+++ b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java
@@ -4,6 +4,7 @@ import com.codesdream.ase.component.permission.*;
import com.codesdream.ase.service.ASEUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
@@ -51,6 +52,7 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
ASEAccessDeniedHandler accessDeniedHandler;
+
@Override
protected void configure(HttpSecurity http) throws Exception {
http
@@ -66,7 +68,9 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
// 替换掉原有的UsernamePasswordAuthenticationFilter
http.addFilterAt(aseUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
- .addFilterAfter(new SecurityContextPersistenceFilter(), UsernamePasswordAuthenticationFilter.class);
+ .addFilterBefore(asejsonTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
+
+ http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@@ -90,6 +94,12 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
"/login/**");
}
+ //注册自定义的UsernamePasswordAuthenticationFilter
+ @Bean
+ ASEJSONTokenAuthenticationFilter asejsonTokenAuthenticationFilter() throws Exception {
+ return new ASEJSONTokenAuthenticationFilter();
+ }
+
//注册自定义的UsernamePasswordAuthenticationFilter
@Bean
ASEUsernamePasswordAuthenticationFilter aseUsernamePasswordAuthenticationFilter() throws Exception {
@@ -99,7 +109,7 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy(sessionRegistry()));
filter.setAllowSessionCreation(true);
filter.setRequiresAuthenticationRequestMatcher(
- new AntPathRequestMatcher("/login/process", "POST"));
+ new AntPathRequestMatcher("/login/token", "POST"));
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
diff --git a/src/main/java/com/codesdream/ase/controller/APIController.java b/src/main/java/com/codesdream/ase/controller/APIController.java
new file mode 100644
index 0000000..f2a574d
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/controller/APIController.java
@@ -0,0 +1,14 @@
+package com.codesdream.ase.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api")
+public class APIController {
+
+ @RequestMapping("hello")
+ String hello(){
+ return "hello";
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/controller/ASEErrorController.java b/src/main/java/com/codesdream/ase/controller/ASEErrorController.java
index d5930ad..124294f 100644
--- a/src/main/java/com/codesdream/ase/controller/ASEErrorController.java
+++ b/src/main/java/com/codesdream/ase/controller/ASEErrorController.java
@@ -18,6 +18,7 @@ import java.util.List;
@Controller
public class ASEErrorController implements ErrorController {
+
@RequestMapping("/error")
public String handleError(HttpServletRequest request, Model model){
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
diff --git a/src/main/java/com/codesdream/ase/controller/LoginController.java b/src/main/java/com/codesdream/ase/controller/LoginController.java
index 33fb19f..9796ea7 100644
--- a/src/main/java/com/codesdream/ase/controller/LoginController.java
+++ b/src/main/java/com/codesdream/ase/controller/LoginController.java
@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
import java.util.Optional;
@@ -41,9 +42,9 @@ public class LoginController {
return "login";
}
- @RequestMapping(value = "/login/check", method = RequestMethod.POST)
+ @RequestMapping(value = "/login/check_exists", method = RequestMethod.POST)
@ResponseBody
- String checkLogin(HttpServletRequest request){
+ String checkExists(HttpServletRequest request){
// 检查是否为JSON
Optional json = jsonParameter.getJSONByRequest(request);
@@ -51,10 +52,11 @@ public class LoginController {
UserLoginChecker loginChecker = json.get().toJavaObject(UserLoginChecker.class);
- // 检查类型
+
+ // 检查学号对应的用户名是否存在
if(loginChecker.getCheckType().equals("UsernameExistChecker")){
// 根据学号计算用户名
- String user = usernameEncoder.encode(loginChecker.getUsername()) ;
+ String user = userService.getUsernameByStudentId(loginChecker.getUsername());
// 查询用户名存在状态
boolean existStatus = userService.checkIfUserExists(user).getKey();
// 构造返回对象
@@ -68,4 +70,29 @@ public class LoginController {
}
}
+ // 根据学号计算对应的username
+ @RequestMapping(value = "/login/check_uid", method = RequestMethod.POST)
+ @ResponseBody
+ String checkUsernameByStudentID(HttpServletRequest request){
+ // 检查是否为JSON
+ Optional json = jsonParameter.getJSONByRequest(request);
+ if(!json.isPresent()) return jsonParameter.getJSONString(new FailedSONRespond());
+
+ UserLoginChecker loginChecker = json.get().toJavaObject(UserLoginChecker.class);
+
+ if(loginChecker.getCheckType().equals("UIDGeneratorChecker")) {
+ UserLoginCheckerJSONRespond respond = new UserLoginCheckerJSONRespond();
+ respond.setRespondInformation(userService.getUsernameByStudentId(loginChecker.getUsername()));
+ return jsonParameter.getJSONString(respond);
+ }
+ else {
+ // 返回失败对象
+ return jsonParameter.getJSONString(new JSONBaseRespondObject());
+ }
+
+
+ }
+
+
+
}
diff --git a/src/main/java/com/codesdream/ase/exception/JSONTokenExpiredException.java b/src/main/java/com/codesdream/ase/exception/JSONTokenExpiredException.java
new file mode 100644
index 0000000..8c90908
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/exception/JSONTokenExpiredException.java
@@ -0,0 +1,9 @@
+package com.codesdream.ase.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class JSONTokenExpiredException extends AuthenticationException {
+ public JSONTokenExpiredException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/exception/JSONTokenIncorrectSignedException.java b/src/main/java/com/codesdream/ase/exception/JSONTokenIncorrectSignedException.java
new file mode 100644
index 0000000..edef8c0
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/exception/JSONTokenIncorrectSignedException.java
@@ -0,0 +1,10 @@
+package com.codesdream.ase.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class JSONTokenIncorrectSignedException extends AuthenticationException {
+
+ public JSONTokenIncorrectSignedException(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/model/auth/JSONToken.java b/src/main/java/com/codesdream/ase/model/auth/JSONToken.java
new file mode 100644
index 0000000..e88c51b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/model/auth/JSONToken.java
@@ -0,0 +1,28 @@
+package com.codesdream.ase.model.auth;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.util.Date;
+
+// token记录
+@Data
+@Entity
+@Table(name = "json_tokens")
+public class JSONToken {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Integer id;
+
+ // token对应的用户
+ @Column(unique = true)
+ private String username;
+
+ // token值
+ @Column(unique = true)
+ private String token;
+
+ // token过期时间
+ private Date expiredDate;
+}
diff --git a/src/main/java/com/codesdream/ase/repository/auth/JSONTokenRepository.java b/src/main/java/com/codesdream/ase/repository/auth/JSONTokenRepository.java
new file mode 100644
index 0000000..a2a4f68
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/repository/auth/JSONTokenRepository.java
@@ -0,0 +1,12 @@
+package com.codesdream.ase.repository.auth;
+
+import com.codesdream.ase.model.auth.JSONToken;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface JSONTokenRepository extends CrudRepository {
+ Optional findByUsername(String username);
+}
diff --git a/src/main/java/com/codesdream/ase/service/AuthService.java b/src/main/java/com/codesdream/ase/service/AuthService.java
new file mode 100644
index 0000000..f88aff7
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/service/AuthService.java
@@ -0,0 +1,57 @@
+package com.codesdream.ase.service;
+
+import com.codesdream.ase.component.auth.AuthTokenGenerator;
+import com.codesdream.ase.model.auth.JSONToken;
+import com.codesdream.ase.model.permission.User;
+import com.codesdream.ase.repository.auth.JSONTokenRepository;
+import com.sun.org.apache.xpath.internal.operations.Bool;
+import javafx.util.Pair;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.Optional;
+
+// 认证服务
+@Service
+public class AuthService implements IAuthService {
+
+ @Resource
+ private JSONTokenRepository jsonTokenRepository;
+
+ @Resource
+ private IUserService userService;
+
+ @Resource
+ private AuthTokenGenerator authTokenGenerator;
+
+ @Override
+ public Optional findTokenByUserName(String username) {
+ return jsonTokenRepository.findByUsername(username);
+ }
+
+ @Override
+ public boolean checkTokenIfExpired(JSONToken token) {
+ return token.getExpiredDate().compareTo(new Date()) <= 0;
+ }
+
+ @Override
+ public Optional userNewTokenGetter(String username) {
+ 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
+ token = jsonTokenRepository.save(token);
+ return Optional.ofNullable(token.getToken());
+ }
+ else return Optional.empty();
+ }
+}
diff --git a/src/main/java/com/codesdream/ase/service/IAuthService.java b/src/main/java/com/codesdream/ase/service/IAuthService.java
new file mode 100644
index 0000000..a3f6a7b
--- /dev/null
+++ b/src/main/java/com/codesdream/ase/service/IAuthService.java
@@ -0,0 +1,16 @@
+package com.codesdream.ase.service;
+
+import com.codesdream.ase.model.auth.JSONToken;
+
+import java.util.Optional;
+
+public interface IAuthService {
+ // 通过用户名查找与对应用户相关联的token
+ Optional findTokenByUserName(String username);
+
+ // 检查token是否过期
+ boolean checkTokenIfExpired(JSONToken token);
+
+ // 为用户获得一个新的API Token
+ Optional userNewTokenGetter(String username);
+}
diff --git a/src/main/java/com/codesdream/ase/service/IUserService.java b/src/main/java/com/codesdream/ase/service/IUserService.java
index 8bfe3fa..06281f4 100644
--- a/src/main/java/com/codesdream/ase/service/IUserService.java
+++ b/src/main/java/com/codesdream/ase/service/IUserService.java
@@ -24,6 +24,7 @@ public interface IUserService {
User findUserByUsername(String username);
+ // 查询用户是否存在
public Pair checkIfUserExists(String username);
// 获得用户所有的权限角色
@@ -38,6 +39,9 @@ public interface IUserService {
// 根据学号生成随机用户名
void generateRandomUsernameByStudentID(User user, String id);
+ // 更具学号获得对应的用户名
+ String getUsernameByStudentId(String studentId);
+
// 随机生成一个用户名
void generateRandomUsername(User user);
diff --git a/src/main/java/com/codesdream/ase/service/UserService.java b/src/main/java/com/codesdream/ase/service/UserService.java
index e621b51..e09ad09 100644
--- a/src/main/java/com/codesdream/ase/service/UserService.java
+++ b/src/main/java/com/codesdream/ase/service/UserService.java
@@ -81,6 +81,11 @@ public class UserService implements IUserService {
user.setUsername(usernameEncoder.encode(id));
}
+ @Override
+ public String getUsernameByStudentId(String studentId) {
+ return usernameEncoder.encode(studentId);
+ }
+
@Override
public void generateRandomUsername(User user) {
user.setUsername(usernameEncoder.encode(UUID.randomUUID().toString()));