일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- STS
- SpringBoot
- Open Source
- PostgreSQL
- MySQL
- jpa
- IntelliJ
- Eclipse
- AJAX
- 설정
- Docker
- Source
- 오픈소스
- Core Java
- myBatis
- MSSQL
- git
- ubuntu
- JavaScript
- oracle
- 문서
- error
- spring
- Tomcat
- Spring Boot
- JDBC
- Python
- Thymeleaf
- maven
- Exception
- Today
- Total
헤르메스 LIFE
Spring Security on REST API 구축 실패기...!!! 본문
개발환경.
ajax + RESTful + Spring Security
시나리오.
RESTful API 환경 구축.
oAuth를 생각해 봤지만, 갑자기 눈에 들어온 것이 Spring Security... 시련의 시작...
1. 시작
그림, 소스 출처 : Spring Security on REST
수많은 검색 끝에 찾아낸 결론이였습니다.
소스도 있고 결과만 확인 하면 됐습니다. ( 뭐.. 항상 그렇지만 쉽지만은 않습니다. )
여차저차해서 전체적으로 약간의 수정으로 테스트가 이뤄졌습니다.
( restServicesEntryPoint 를 추가해 넣었습니다. )
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Rest authentication entry point configuration -->
<http use-expressions="true" entry-point-ref="restServicesEntryPoint">
<intercept-url pattern="/api/**" />
<sec:form-login username-parameter="username"
password-parameter="password"
authentication-success-handler-ref="mySuccessHandler"
authentication-failure-handler-ref="myFailureHandler" />
<logout />
</http>
<!-- 인증 entry point 설정 - 인증 실패 시 401 Return -->
<beans:bean id="restServicesEntryPoint" class="rest.api.security.RestAuthenticationEntryPoint" />
<!-- Connect the custom authentication success handler -->
<beans:bean id="mySuccessHandler" class="rest.api.security.RestAuthenticationSuccessHandler" />
<!-- Using default failure handler -->
<beans:bean id="myFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler" />
<!-- Authentication manager -->
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="temporary" password="temporary" authorities="ROLE_ADMIN" />
<user name="user" password="userPass" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<!-- Enable the annotations for defining the secure role -->
<global-method-security secured-annotations="enabled" />
</beans:beans>
HttpClient를 이용해서 POST 방식으로 인증하고, 그 인증된 권한으로 실제요청을 하는 시나리오는 성공했습니다.
public static void test01() throws Exception {
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(loginUrl);
Map<String, String> parameterMap = new HashMap<String, String>();
parameterMap.put("username", "temporary");
parameterMap.put("password", "temporary");
UrlEncodedFormEntity postEntity = new UrlEncodedFormEntity(getParam(parameterMap), "UTF-8");
httpPost.setEntity(postEntity);
//System.out.println("request line:" + httpPost.getRequestLine());
try {
HttpResponse httpResponse = client.execute(httpPost);
//status:HTTP/1.1 401 Unauthorized
System.out.println("Status :: " + httpResponse.getStatusLine());
printResponse(httpResponse);
System.out.println("----the same client");
HttpGet httpGet = new HttpGet(testUrl);
System.out.println("request line:" + httpGet.getRequestLine());
HttpResponse httpResponse1 = client.execute(httpGet);
printResponse(httpResponse1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
request line:http://localhost:8080/j_spring_security_check HTTP/1.1
status:HTTP/1.1 200 OK
headers:
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=AE5211F53CB844576E8171A4D86422C3; Path=/; HttpOnly
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Vary: Accept-Encoding
Date: Wed, 24 Aug 2016 11:32:35 GMT
response length:0
response content:
----the same client
request line:POST http://localhost:8080/api/employees HTTP/1.1
status:HTTP/1.1 200 OK
headers:
Server: Apache-Coyote/1.1
Cache-Control: no-cache
Content-Type: application/json;charset=UTF-8
Content-Language: ko
Transfer-Encoding: chunked
Vary: Accept-Encoding
Date: Wed, 24 Aug 2016 11:32:35 GMT
response length:1084
response content:[{"MASTER_ID":"","ZIP_CODE":"1111","BIRTHDAY":"","ID":"CUST001","ADDR_TYPE":"","ADDR2":"주소2","ADDR1":"서울시 마포구","EMAIL":"hong@test.com","CUST_BIGO":"","NAME":"홍길동","MOBILE":"010111111111","TEL":"0211111111"},{"MASTER_ID":"","ZIP_CODE":"2222","BIRTHDAY":"","ID":"CUST002","ADDR_TYPE":"","ADDR2":"주소2","ADDR1":"주소1","EMAIL":"hong@test.com","CUST_BIGO":"","NAME":"허균","MOBILE":"01022222222","TEL":"0222222222"}]
뭐... 글쓰신 분도 curl 로 테스트 하셨을 때는 잘 된다. 하시던군요..
맞습니다. 잘 됩니다...
그런데...
ajax 로 하면 안됩니다. ㅡㅡ;;
처음엔 CORS 오류가 저를 괴롭혔습니다. 그래서 여차저차해서 몇일을 고생해서 해결했습니다. ( 참조 : [CORS] javascript ajax 크로스 도메인 요청 하기 )
authRequest("http://localhost:8080/j_spring_security_check", {
"username" : "temporary",
"password" : "temporary"
}, function(xhr, statusText) {
console.log("인증 결과 :: " + statusText);
console.log("인증 status :: " + xhr.status);
//if(xhr.status == 200) {
if(xhr.status == 0) {
ajaxRequest("http://localhost:8080/api/employees", {
"param", "param"
});
}
});
function authRequest(baseurl, params, onSuccess)
{
$.ajax({
url : baseurl
, async : true
, type : "POST"
, cache:false
, timeout : 30000
, data : JSON.parse(JSON.stringify(params))
, dataType : "json"
, crossDomain: true
, xhrFields: {
withCredentials: true
}
, beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "application/json");
}
, complete : function (xhr, statusText) {
onSuccess(xhr, statusText)
}
, error: function(xhr, statusText, errorThrown) {
console.log("xhr :: " + xhr.status);
console.log("statusText :: " + statusText);
console.log("errorThrown :: " + errorThrown);
}
});
}
function ajaxRequest(baseurl, params)
{
$.ajax({
url : "http://localhost:8080/api/employees"
, async : true
, type : "POST"
, cache:false
, timeout : 30000
, data : JSON.parse(JSON.stringify(params))
, dataType : "json"
, xhrFields: {
withCredentials: true
}
, beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "application/json");
}
, success : function (data, statusText, xhr) {
console.log("success status :: " + xhr.status + " || statusText :: " + statusText);
}
, error : function (xhr, statusText, errorThrown) {
console.log("error status :: " + xhr.status + " || statusText :: " + xhr.responseText + " || errorThrown :: " + errorThrown);
}
});
}
테스트 때 걸렸던 내용..
1. withCredentials: true 를 사용한 이유...
jsessionid를 같이 사용하기 위해 사용했습니다.
한 트랜잭션 안에서 실행이 되어야 할 듯 합니다.
왜냐하면 권한을 인증하고, 그 인증된 결과를 가지고 다시 실제요청을 해야합니다. ( 달리 방법을 찾지 못했습니다. 방법이 있을까요..? )
2. CORS
Filter를 적용해서 해결했습니다. ( 공부좀 했져.. ^^;; )
XMLHttpRequest cannot load http://localhost:8080/accnt/api/employees. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:7070' is therefore not allowed access.
ajax를 이용해서 인증 + 실제요청을 실패한 이유..
1 번 이유와 2번 이유가 충돌이 납니다.
1번 이유 때문에 withCredentials: true 를 정의했는데..
CORS 때문에 Filter를 적용하고 나면 인증 쪽에서 Access Denied 가 떨어집니다.
CORS Filter를 막으면 withCredentials: true 가 정상적으로 동작을 합니다.
인증도 정상적으로 처리됩니다.
Service 영역에서 조회되서 결과를 Return 하는 결과까지 확인이 됩니다.
문제는 CORS 때문인지 xhr.status 가 0 이 됩니다. ( 여러가지 이유가 있겠죠.. )
현재로서는 해결의 방법이 없습니다. ㅠ.ㅠ
이제 생각할 수 있는 방법은..
ajax + CORS Filter + oAuth 를 이용해서 RESTful API 환경을 구축하는 겁니다.
그래도 malalanayake ( http://ko.gravatar.com/malalanayake ) 께 감사드립니다.
자신의 소스를 전부 내주셨으니 덕분에 테스트를 해 볼 수 있었습니다.
'Spring Framework' 카테고리의 다른 글
[Spring] log4jdbc-remix 를 이용한 쿼리 로그 출력하기 (0) | 2020.12.06 |
---|---|
[전자정부프레임워크] EgovPropertyServiceImpl (0) | 2020.11.12 |
Spring + JSON 설정 시 유의사항 (0) | 2016.08.16 |
[Source] HTML Parsing Source (0) | 2016.03.29 |
[Spring MVC] cache-period 설정 (1) | 2016.03.15 |