为认证添加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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<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;
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));
}
}

View File

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

View File

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

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,48 +2,45 @@ 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("UserLoginChecker"))
if (!checker.getCheckType().equals("From"))
throw new AuthenticationServiceException("Invalid Checker Type");
String username = checker.getUsername();
@ -55,11 +52,17 @@ public class ASEUsernamePasswordAuthenticationFilter extends AbstractAuthenticat
// 去除首尾两端的空白字符
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
UsernamePasswordAuthenticationToken authRequest =
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);
}
else{
return super.attemptAuthentication(request, response);
}
}
}

View File

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

View File

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

View File

@ -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 框架全局配置类

View File

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

View File

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

View File

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

View File

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