为认证添加JSON支持

This commit is contained in:
Saturneric 2020-03-01 15:13:16 +08:00
parent 837b311eb4
commit 2b2f434917
15 changed files with 206 additions and 101 deletions

View File

@ -79,11 +79,6 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId> <artifactId>spring-boot-test</artifactId>

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -1,6 +1,7 @@
package com.codesdream.ase.component.permission; package com.codesdream.ase.component.permission;
import com.codesdream.ase.component.datamanager.JSONParameter; import com.codesdream.ase.component.datamanager.JSONParameter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
@ -13,6 +14,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
// 认证失败返回
@Slf4j
@Component @Component
public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@ -23,10 +26,14 @@ public class ASEAuthenticationFailureHandler extends SimpleUrlAuthenticationFail
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException throws IOException, ServletException
{ {
logger.info("ASEAuthenticationSuccessHandler Login Fail!"); log.info("ASEAuthenticationSuccessHandler Login Fail!");
UserLoginCheckerRespond respond = new UserLoginCheckerRespond(); UserLoginCheckerRespond respond = new UserLoginCheckerRespond();
respond.setUserExist(false); respond.setUserExist(false);
respond.setLoginStatus(false); respond.setLoginStatus(false);
respond.setUserBanned(true);
respond.setRespondInformation("Authentication Failed");
// 填充response对象
response.getWriter().write(jsonParameter.getJSONString(respond)); response.getWriter().write(jsonParameter.getJSONString(respond));
} }
} }

View File

@ -1,6 +1,7 @@
package com.codesdream.ase.component.permission; package com.codesdream.ase.component.permission;
import com.codesdream.ase.component.datamanager.JSONParameter; import com.codesdream.ase.component.datamanager.JSONParameter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -10,7 +11,10 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.security.Principal;
// 认证成功返回
@Slf4j
@Component @Component
public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Resource @Resource
@ -20,10 +24,12 @@ public class ASEAuthenticationSuccessHandler extends SavedRequestAwareAuthentica
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException throws IOException, ServletException
{ {
logger.info("ASEAuthenticationSuccessHandler Login Success!"); log.info("ASEAuthenticationSuccessHandler Login Success!");
UserLoginCheckerRespond respond = new UserLoginCheckerRespond(); UserLoginCheckerRespond respond = new UserLoginCheckerRespond();
respond.setUserExist(true); respond.setUserExist(authentication.isAuthenticated());
respond.setLoginStatus(true); respond.setLoginStatus(authentication.isAuthenticated());
// 填充response对象
response.getWriter().write(jsonParameter.getJSONString(respond)); response.getWriter().write(jsonParameter.getJSONString(respond));
/* response.sendRedirect("/home");*/
} }
} }

View File

@ -34,6 +34,9 @@ public class ASESecurityAuthenticationProvider implements AuthenticationProvider
String password = passwordEncoder.encode(authentication.getCredentials().toString()); String password = passwordEncoder.encode(authentication.getCredentials().toString());
// 判断用户是否存在 // 判断用户是否存在
UserDetails userInfo = userDetailsService.loadUserByUsername(username); UserDetails userInfo = userDetailsService.loadUserByUsername(username);
log.info(String.format("SecurityAuthentication: %s %s", username, password));
if (userInfo == null) { if (userInfo == null) {
throw new UsernameNotFoundException("User IS Not Existing"); throw new UsernameNotFoundException("User IS Not Existing");
} }

View File

@ -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);
}
}

View File

@ -2,64 +2,67 @@ package com.codesdream.ase.component.permission;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.codesdream.ase.component.datamanager.JSONParameter; 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.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; 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.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.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException; import java.io.IOException;
// 登录验证过滤器 // 登录验证过滤器
public class ASEUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { @Slf4j
// 常量 public class ASEUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
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;
@Resource @Resource
private JSONParameter jsonParameter; private JSONParameter jsonParameter;
protected ASEUsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher(SPRING_SECURITY_RESTFUL_LOGIN_URL, "POST"));
}
@Override @Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException { throws AuthenticationException {
// 检查提交方式 // 判断是否为JSON格式的数据
if (postOnly && !request.getMethod().equals("POST")) { log.info(String.format("Content Type: %s", request.getContentType()));
throw new AuthenticationServiceException( if(request.getContentType().equals("application/x-www-form-urlencoded; charset=UTF-8")) {
"Authentication Method NOT Supported: " + request.getMethod());
}
UserLoginChecker checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class); UserLoginChecker checker = jsonParameter.getJavaObjectByRequest(request, UserLoginChecker.class);
if(!checker.getCheckType().equals("UserLoginChecker")) if (!checker.getCheckType().equals("From"))
throw new AuthenticationServiceException("Invalid Checker Type"); throw new AuthenticationServiceException("Invalid Checker Type");
String username = checker.getUsername(); String username = checker.getUsername();
String password = checker.getPassword(); String password = checker.getPassword();
if(username == null) username = ""; if (username == null) username = "";
if(password == null) password = ""; if (password == null) password = "";
// 去除首尾两端的空白字符 // 去除首尾两端的空白字符
username = username.trim(); username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( UsernamePasswordAuthenticationToken authRequest =
username, password); new UsernamePasswordAuthenticationToken(username, password);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); log.info(String.format("User Authentication: %s %s.", username, password));
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest); return this.getAuthenticationManager().authenticate(authRequest);
} }
else{
return super.attemptAuthentication(request, response);
}
}
} }

View File

@ -9,6 +9,8 @@ import lombok.EqualsAndHashCode;
public class UserLoginCheckerRespond extends RespondJSONBaseObject { public class UserLoginCheckerRespond extends RespondJSONBaseObject {
boolean userExist = false; boolean userExist = false;
boolean loginStatus = false; boolean loginStatus = false;
boolean userBanned = false;
String respondInformation = "";
public UserLoginCheckerRespond(){ public UserLoginCheckerRespond(){
super("success"); super("success");

View File

@ -1,14 +1,16 @@
package com.codesdream.ase.configure; package com.codesdream.ase.configure;
import com.codesdream.ase.component.permission.ASEPasswordEncoder; import com.codesdream.ase.component.permission.*;
import com.codesdream.ase.component.permission.ASESecurityAuthenticationProvider;
import com.codesdream.ase.service.ASEUserDetailsService; import com.codesdream.ase.service.ASEUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity; 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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 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; import javax.annotation.Resource;
@ -29,18 +31,33 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource @Resource
ASESecurityAuthenticationProvider aseSecurityAuthenticationProvider; ASESecurityAuthenticationProvider aseSecurityAuthenticationProvider;
@Resource
ASEAuthenticationSuccessHandler successHandler;
@Resource
ASEAuthenticationFailureHandler failureHandler;
@Resource
ASEAuthenticationEntryPoint authenticationEntryPoint;
@Resource
ASEAccessDeniedHandler accessDeniedHandler;
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() http
.authorizeRequests()
.anyRequest().authenticated() .anyRequest().authenticated()
.and() .and()
.csrf().disable().formLogin() .csrf().disable()
.and()
.formLogin().loginPage("/login")
.permitAll().defaultSuccessUrl("/home").permitAll()
.and()
.logout().permitAll(); .logout().permitAll();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
// 替换掉原有的UsernamePasswordAuthenticationFilter
http.addFilterBefore(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
} }
@Override @Override
@ -62,4 +79,18 @@ public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
"/error/**", "/error/**",
"/login/**"); "/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;
}
} }

View File

@ -1,12 +1,7 @@
package com.codesdream.ase.configure; 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 org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/** /**
* Spring 框架全局配置类 * Spring 框架全局配置类

View File

@ -18,3 +18,5 @@ spring.datasource.password=codedreampasswd
server.error.whitelabel.enabled=false server.error.whitelabel.enabled=false
logging.level.org.springframework.security=DEBUG

View File

@ -146,16 +146,34 @@ function callback_decorator(callback){
return 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方式提交表单 // 快速以post方式提交表单
function ase_form_post(url ,id, callback){ function ase_form_post(url ,id, callback){
const form = ase_tag_getter("form", id);
callback = callback_decorator(callback); callback = callback_decorator(callback);
const form_object = ase_serialize_object(ase_tag_getter("form", id));
form_object.checkType = "From";
$.ajax({ $.ajax({
type: "POST", type: "POST",
dataType: "json", dataType: "json",
url: url , url: url ,
data: form.serialize(), data: JSON.stringify(form_object),
success: callback.success, success: callback.success,
error : callback.error, error : callback.error,
}); });

View File

@ -16,7 +16,7 @@ function notify_check() {
} }
function login() { function login() {
ase_form_post("/login","login-form", { ase_form_post("/login/process","login-form", {
success : function (result) { success : function (result) {
console.log(result); console.log(result);
}, },

View File

@ -17,7 +17,7 @@
<div class="card-header"> <div class="card-header">
<h4 class="card-title">登录</h4> <h4 class="card-title">登录</h4>
</div> </div>
<form id="login-form" action="/login" method="post"> <form id="login-form" action="/login/process" method="post">
<div class="card-body"> <div class="card-body">
<div id="username-group" class="form-group"> <div id="username-group" class="form-group">
<label for="username">学号</label> <label for="username">学号</label>