From 2b2f4349174df855e0136000d0eedb763c0e9071 Mon Sep 17 00:00:00 2001 From: Saturneric Date: Sun, 1 Mar 2020 15:13:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E8=AE=A4=E8=AF=81=E6=B7=BB=E5=8A=A0JS?= =?UTF-8?q?ON=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 -- .../permission/ASEAccessDeniedHandler.java | 39 +++++++++ .../ASEAuthenticationEntryPoint.java | 39 +++++++++ .../ASEAuthenticationFailureHandler.java | 9 ++- .../ASEAuthenticationSuccessHandler.java | 12 ++- .../ASESecurityAuthenticationProvider.java | 3 + .../permission/ASEUnauthorizedEntryPoint.java | 35 -------- ...EUsernamePasswordAuthenticationFilter.java | 79 ++++++++++--------- .../permission/UserLoginCheckerRespond.java | 2 + .../configure/CustomWebSecurityConfig.java | 47 +++++++++-- .../ase/configure/GlobalConfigure.java | 7 +- src/main/resources/application.properties | 2 + .../assets/js/ase/ase-jquery-framework.js | 24 +++++- .../static/assets/js/ase/ase-login.js | 2 +- src/main/resources/templates/login.html | 2 +- 15 files changed, 206 insertions(+), 101 deletions(-) create mode 100644 src/main/java/com/codesdream/ase/component/permission/ASEAccessDeniedHandler.java create mode 100644 src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java delete mode 100644 src/main/java/com/codesdream/ase/component/permission/ASEUnauthorizedEntryPoint.java diff --git a/pom.xml b/pom.xml index 47427d0..b66f69d 100644 --- a/pom.xml +++ b/pom.xml @@ -79,11 +79,6 @@ compile - - org.springframework.boot - spring-boot-starter-websocket - - org.springframework.boot spring-boot-test diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEAccessDeniedHandler.java b/src/main/java/com/codesdream/ase/component/permission/ASEAccessDeniedHandler.java new file mode 100644 index 0000000..5374ff9 --- /dev/null +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAccessDeniedHandler.java @@ -0,0 +1,39 @@ +package com.codesdream.ase.component.permission; + +import com.codesdream.ase.component.datamanager.JSONParameter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +// 认证用户访问无权限资源 +@Slf4j +@Component +public class ASEAccessDeniedHandler implements AccessDeniedHandler { + + @Resource + private JSONParameter jsonParameter; + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) + throws IOException, ServletException { + log.info("ASEAccessDeniedHandler Found!"); + + response.setCharacterEncoding("utf-8"); + response.setContentType("text/javascript;charset=utf-8"); + UserLoginCheckerRespond checkerRespond = new UserLoginCheckerRespond(); + checkerRespond.setLoginStatus(true); + checkerRespond.setUserExist(true); + checkerRespond.setRespondInformation("Authenticated user has no access to this resource"); + + // 对匿名用户返回 + response.getWriter().print(jsonParameter.getJSONString(checkerRespond)); + + } +} diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java new file mode 100644 index 0000000..d3f6807 --- /dev/null +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationEntryPoint.java @@ -0,0 +1,39 @@ +package com.codesdream.ase.component.permission; + +import com.codesdream.ase.component.datamanager.JSONParameter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +// 匿名用户访问无权限资源 +@Slf4j +@Component +public class ASEAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Resource + private JSONParameter jsonParameter; + + @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"); + UserLoginCheckerRespond checkerRespond = new UserLoginCheckerRespond(); + 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)); + + } +} 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 632a4af..a231c1c 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationFailureHandler.java @@ -1,6 +1,7 @@ package com.codesdream.ase.component.permission; import com.codesdream.ase.component.datamanager.JSONParameter; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; @@ -13,6 +14,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +// 认证失败返回 +@Slf4j @Component public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @@ -23,10 +26,14 @@ public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFail public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - logger.info("ASEAuthenticationSuccessHandler Login Fail!"); + log.info("ASEAuthenticationSuccessHandler Login Fail!"); UserLoginCheckerRespond respond = new UserLoginCheckerRespond(); respond.setUserExist(false); respond.setLoginStatus(false); + respond.setUserBanned(true); + respond.setRespondInformation("Authentication Failed"); + + // 填充response对象 response.getWriter().write(jsonParameter.getJSONString(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 e91adea..f867824 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEAuthenticationSuccessHandler.java @@ -1,6 +1,7 @@ package com.codesdream.ase.component.permission; import com.codesdream.ase.component.datamanager.JSONParameter; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.stereotype.Component; @@ -10,7 +11,10 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.security.Principal; +// 认证成功返回 +@Slf4j @Component public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Resource @@ -20,10 +24,12 @@ public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthentica public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - logger.info("ASEAuthenticationSuccessHandler Login Success!"); + log.info("ASEAuthenticationSuccessHandler Login Success!"); UserLoginCheckerRespond respond = new UserLoginCheckerRespond(); - respond.setUserExist(true); - respond.setLoginStatus(true); + respond.setUserExist(authentication.isAuthenticated()); + respond.setLoginStatus(authentication.isAuthenticated()); + // 填充response对象 response.getWriter().write(jsonParameter.getJSONString(respond)); +/* response.sendRedirect("/home");*/ } } 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 243db1b..8e9dcf3 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASESecurityAuthenticationProvider.java @@ -34,6 +34,9 @@ public class ASESecurityAuthenticationProvider implements AuthenticationProvider String password = passwordEncoder.encode(authentication.getCredentials().toString()); // 判断用户是否存在 UserDetails userInfo = userDetailsService.loadUserByUsername(username); + + log.info(String.format("SecurityAuthentication: %s %s", username, password)); + if (userInfo == null) { throw new UsernameNotFoundException("User IS Not Existing"); } diff --git a/src/main/java/com/codesdream/ase/component/permission/ASEUnauthorizedEntryPoint.java b/src/main/java/com/codesdream/ase/component/permission/ASEUnauthorizedEntryPoint.java deleted file mode 100644 index 9ced87e..0000000 --- a/src/main/java/com/codesdream/ase/component/permission/ASEUnauthorizedEntryPoint.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.codesdream.ase.component.permission; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@Slf4j -@Component -public class ASEUnauthorizedEntryPoint implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) - throws IOException, ServletException - { - - if(isAjaxRequest(request)){ - log.info("IS Ajax Request."); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage()); - }else{ - log.info("IS NOT Ajax Request."); - response.sendRedirect("/login"); - } - } - - public static boolean isAjaxRequest(HttpServletRequest request) { - String ajaxFlag = request.getHeader("X-Requested-With"); - return "XMLHttpRequest".equals(ajaxFlag); - } -} 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 e079327..5aa95e8 100644 --- a/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java +++ b/src/main/java/com/codesdream/ase/component/permission/ASEUsernamePasswordAuthenticationFilter.java @@ -2,64 +2,67 @@ package com.codesdream.ase.component.permission; import com.alibaba.fastjson.JSONObject; import com.codesdream.ase.component.datamanager.JSONParameter; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.TextEscapeUtils; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import java.io.IOException; // 登录验证过滤器 -public class ASEUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - // 常量 - public static final String SPRING_SECURITY_RESTFUL_USERNAME_KEY = "username"; - public static final String SPRING_SECURITY_RESTFUL_PASSWORD_KEY = "password"; - public static final String SPRING_SECURITY_RESTFUL_LOGIN_URL = "/login"; - - private String usernameParameter = SPRING_SECURITY_RESTFUL_USERNAME_KEY; - private String passwordParameter = SPRING_SECURITY_RESTFUL_PASSWORD_KEY; - private boolean postOnly = true; +@Slf4j +public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Resource private JSONParameter jsonParameter; - protected ASEUsernamePasswordAuthenticationFilter() { - super(new AntPathRequestMatcher(SPRING_SECURITY_RESTFUL_LOGIN_URL, "POST")); - } - @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException, IOException, ServletException { - // 检查提交方式 - if (postOnly && !request.getMethod().equals("POST")) { - throw new AuthenticationServiceException( - "Authentication Method NOT Supported: " + request.getMethod()); + throws AuthenticationException { + // 判断是否为JSON格式的数据 + log.info(String.format("Content Type: %s", request.getContentType())); + if(request.getContentType().equals("application/x-www-form-urlencoded; charset=UTF-8")) { + + UserLoginChecker checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class); + if (!checker.getCheckType().equals("From")) + throw new AuthenticationServiceException("Invalid Checker Type"); + + String username = checker.getUsername(); + String password = checker.getPassword(); + + if (username == null) username = ""; + if (password == null) password = ""; + + // 去除首尾两端的空白字符 + username = username.trim(); + + UsernamePasswordAuthenticationToken authRequest = + new UsernamePasswordAuthenticationToken(username, password); + + log.info(String.format("User Authentication: %s %s.", username, password)); + + setDetails(request, authRequest); + + return this.getAuthenticationManager().authenticate(authRequest); + } + else{ + return super.attemptAuthentication(request, response); } - - UserLoginChecker checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class); - if(!checker.getCheckType().equals("UserLoginChecker")) - throw new AuthenticationServiceException("Invalid Checker Type"); - - String username = checker.getUsername(); - String password = checker.getPassword(); - - if(username == null) username = ""; - if(password == null) password = ""; - - // 去除首尾两端的空白字符 - username = username.trim(); - - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( - username, password); - - authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); - - return this.getAuthenticationManager().authenticate(authRequest); } } diff --git a/src/main/java/com/codesdream/ase/component/permission/UserLoginCheckerRespond.java b/src/main/java/com/codesdream/ase/component/permission/UserLoginCheckerRespond.java index 59e2fb9..13163a1 100644 --- a/src/main/java/com/codesdream/ase/component/permission/UserLoginCheckerRespond.java +++ b/src/main/java/com/codesdream/ase/component/permission/UserLoginCheckerRespond.java @@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode; public class UserLoginCheckerRespond extends RespondJSONBaseObject { boolean userExist = false; boolean loginStatus = false; + boolean userBanned = false; + String respondInformation = ""; public UserLoginCheckerRespond(){ super("success"); diff --git a/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java index 68a032d..279b3c4 100644 --- a/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java +++ b/src/main/java/com/codesdream/ase/configure/CustomWebSecurityConfig.java @@ -1,14 +1,16 @@ package com.codesdream.ase.configure; -import com.codesdream.ase.component.permission.ASEPasswordEncoder; -import com.codesdream.ase.component.permission.ASESecurityAuthenticationProvider; +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.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import javax.annotation.Resource; @@ -29,18 +31,33 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource ASESecurityAuthenticationProvider aseSecurityAuthenticationProvider; + @Resource + ASEAuthenticationSuccessHandler successHandler; + + @Resource + ASEAuthenticationFailureHandler failureHandler; + + @Resource + ASEAuthenticationEntryPoint authenticationEntryPoint; + + @Resource + ASEAccessDeniedHandler accessDeniedHandler; + @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests() + http + .authorizeRequests() .anyRequest().authenticated() .and() - .csrf().disable().formLogin() - .and() - .formLogin().loginPage("/login") - .permitAll().defaultSuccessUrl("/home").permitAll() - .and() + .csrf().disable() .logout().permitAll(); + http.exceptionHandling() + .authenticationEntryPoint(authenticationEntryPoint) + .accessDeniedHandler(accessDeniedHandler); + + // 替换掉原有的UsernamePasswordAuthenticationFilter + http.addFilterBefore(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Override @@ -62,4 +79,18 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter { "/error/**", "/login/**"); } + + //注册自定义的UsernamePasswordAuthenticationFilter + @Bean + ASEUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception { + ASEUsernamePasswordAuthenticationFilter filter = new ASEUsernamePasswordAuthenticationFilter(); + filter.setAuthenticationSuccessHandler(successHandler); + filter.setAuthenticationFailureHandler(failureHandler); + filter.setRequiresAuthenticationRequestMatcher( + new AntPathRequestMatcher("/login/process", "POST")); + + + filter.setAuthenticationManager(authenticationManagerBean()); + return filter; + } } diff --git a/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java b/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java index e39a0e2..a6efbb8 100644 --- a/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java +++ b/src/main/java/com/codesdream/ase/configure/GlobalConfigure.java @@ -1,12 +1,7 @@ package com.codesdream.ase.configure; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.thymeleaf.spring5.view.ThymeleafViewResolver; -import javax.annotation.Resource; -import java.util.HashMap; -import java.util.Map; +import org.springframework.context.annotation.Configuration; /** * Spring 框架全局配置类 diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index be36823..e887681 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,3 +18,5 @@ spring.datasource.password=codedreampasswd server.error.whitelabel.enabled=false +logging.level.org.springframework.security=DEBUG + diff --git a/src/main/resources/static/assets/js/ase/ase-jquery-framework.js b/src/main/resources/static/assets/js/ase/ase-jquery-framework.js index 23f25fb..d5ec105 100644 --- a/src/main/resources/static/assets/js/ase/ase-jquery-framework.js +++ b/src/main/resources/static/assets/js/ase/ase-jquery-framework.js @@ -146,16 +146,34 @@ function callback_decorator(callback){ return callback; } +function ase_serialize_object(form) +{ + const o = {}; + const a = form.serializeArray(); + $.each(a, function() { + if (o[this.name] !== undefined) { + if (!o[this.name].push) { + o[this.name] = [o[this.name]]; + } + o[this.name].push(this.value || ''); + } else { + o[this.name] = this.value || ''; + } + }); + return o; +} + + // 快速以post方式提交表单 function ase_form_post(url ,id, callback){ - const form = ase_tag_getter("form", id); callback = callback_decorator(callback); - + const form_object = ase_serialize_object(ase_tag_getter("form", id)); + form_object.checkType = "From"; $.ajax({ type: "POST", dataType: "json", url: url , - data: form.serialize(), + data: JSON.stringify(form_object), success: callback.success, error : callback.error, }); diff --git a/src/main/resources/static/assets/js/ase/ase-login.js b/src/main/resources/static/assets/js/ase/ase-login.js index 65662a0..a053974 100644 --- a/src/main/resources/static/assets/js/ase/ase-login.js +++ b/src/main/resources/static/assets/js/ase/ase-login.js @@ -16,7 +16,7 @@ function notify_check() { } function login() { - ase_form_post("/login","login-form", { + ase_form_post("/login/process","login-form", { success : function (result) { console.log(result); }, diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 4f94998..8372f2d 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -17,7 +17,7 @@

登录

-
+