1. POM.XML 파일에 라이브러리를 추가.
<!-- 시큐리티 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
이것이 안되면 버전을 4. 대로 버전을 올려 줘야 한다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
2. src/main/resource 에 config 폴서들 생성후 config.properties 파일 추가
# spring security password encoder (default: bCryptPasswordEncoder, shaPasswordEncoder, standardPasswordEncoder)
security.password.encoder = bCryptPasswordEncoder
# spring security password encoder: only shaPasswordEncoder value
security.password.sha = 1
security.password.encodeHashAsBase64 = true
3. WEB.XML 수정
!-- 스프링의 환경설정 파일 로딩 -->
<!-- /WEB-INF/spring/security-context.xml 추가 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security-context.xml
</param-value>
</context-param>
<!-- Security filter 추가 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4. root-context.xml 에 추가
<util:properties id="mei" location="classpath:config/config.properties"/>
5. security-context.xml 파일 추가
기본 설정 방법은
여기에서는 암호화 를 위한 빈 주입만 설정 해도 된다.
패키지명을 자신의 프로젝트에 맞게 변경 할것.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<beans:bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<beans:bean id="shaPasswordEncoder" class="com.java.ex.SHAPasswordEncoder">
<beans:constructor-arg value="#{mei['security.password.sha']}" />
<beans:property name="encodeHashAsBase64" value="#{mei['security.password.encodeHashAsBase64']}" />
</beans:bean>
<beans:bean id="standardPasswordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>
<beans:bean id="passwordEncoder" class="com.java.ex.PasswordEncoding">
<beans:constructor-arg ref="#{mei['security.password.encoder']}"/>
</beans:bean>
</beans:beans>
6. WEB.XML 수정
<!-- 스프링의 환경설정 파일 로딩 -->
<!-- /WEB-INF/spring/security-context.xml 추가 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security-context.xml
</param-value>
</context-param>
<!-- Security filter 추가 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
또는 지금은 암호화만 고려 할 것이므로
root-context.xml 에서 임포트만 해 줘도 된다.
root-context.xml
<util:properties id="mei" location="classpath:config/config.properties"/>
<import resource="appServlet/security-context.xml"/>
7. 파일 2개를 추가 한다.
1
package com.example.wbe04.util.password;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author Seok Kyun. Choi. 최석균 (Syaku)
* @site http://syaku.tistory.com
* @since 16. 2. 18.
*/
public class SHAPasswordEncoder implements PasswordEncoder {
private ShaPasswordEncoder shaPasswordEncoder;
private Object salt = null;
public SHAPasswordEncoder() {
shaPasswordEncoder = new ShaPasswordEncoder();
}
public SHAPasswordEncoder(int sha) {
shaPasswordEncoder = new ShaPasswordEncoder(sha);
}
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
shaPasswordEncoder.setEncodeHashAsBase64(encodeHashAsBase64);
}
public void setSalt(Object salt) {
this.salt = salt;
}
@Override
public String encode(CharSequence rawPassword) {
return shaPasswordEncoder.encodePassword(rawPassword.toString(), salt);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return shaPasswordEncoder.isPasswordValid(encodedPassword, rawPassword.toString(), salt);
}
}
2
package com.example.wbe04.util.password;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author Seok Kyun. Choi. 최석균 (Syaku)
* @site http://syaku.tistory.com
* @since 16. 2. 18.
*/
public class PasswordEncoding implements PasswordEncoder {
private PasswordEncoder passwordEncoder;
public PasswordEncoding() {
this.passwordEncoder = new BCryptPasswordEncoder();
}
public PasswordEncoding(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public String encode(CharSequence rawPassword) {
return passwordEncoder.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
8. 테스트 및 사용
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.example.wbe04.util.encoder.PasswordEncoding;
import com.example.wbe04.util.encoder.SHAPasswordEncoder;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class PasswordTest {
@Inject
PasswordEncoding passwordEncoding;
@Test
public void test2() {
// 1234 라 할지라도 매번 다른 값으로 랜덤하게 encoding 된다.
String encode=passwordEncoding.encode("1234");
// encode 를 DB에 저장 하면 된다.
//값을 비교 하는 것은 matches() 를 사용해서
//로그인 유저와 DB에 저장된 비번을 비교 해서 true 값 이면 비번이 일치 한다는 것이다.
passwordEncoding.matches("1234", encode);
System.out.println("passwordEncoding.encode(1234)" +encode);
System.out.println("1234 비교: " + passwordEncoding.matches("1234", encode));
}
}
9. 프로퍼티 설정 및 root-context.xml 설정 및 security-context.xml 필요 없이 객체를 직접 생성 하려면
아래와 같은 클래스를 만든 후
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
public class EncodPassword {
private static PasswordEncoding passwordEncoding;
//DB 에 변경해서 저장될 암호
public static String dbSavePassword(String encodePasswod){
PasswordEncoder passwordEncoder =new BCryptPasswordEncoder();;
passwordEncoding = new PasswordEncoding(passwordEncoder);
return passwordEncoding.encode(encodePasswod);
}
public static PasswordEncoding getPasswordEncoding() {
return passwordEncoding;
}
}
다음 과 같이 사용 하면 된다.
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.example.wbe04.util.encoder.EncodPassword;
import com.example.wbe04.util.encoder.PasswordEncoding;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class PasswordTest {
@Test
public void passTest() throws Exception{
String dbpass =EncodPassword.dbSavePassword("1234");
System.out.println("DB에 저장 될 암호 ex) 1234: " + dbpass);
// EncodPassword.dbPassword() 만 호출해서 반환 값만 DB 에 저장하면 된다.
// 같은 값이 들어가도 반환 된 값은 랜덤하게 출력 된다.
// 테스트 로그인 할 유저 매치 값 비교하기 true 로 나오면 설정 완료
System.out.println("값 비교: " + EncodPassword.getPasswordEncoding().matches("1234", dbpass));
}
}
끝
아래 파일은 최석균 개발자가 샘플 test (참조용)
public class PasswordEncoderTest {
@Test
public void test() {
String password = "1234";
// SHA-256 암호화를 사용한다
SHAPasswordEncoder shaPasswordEncoder = new SHAPasswordEncoder(512);
shaPasswordEncoder.setEncodeHashAsBase64(true);
PasswordEncoding passwordEncoding = new PasswordEncoding(shaPasswordEncoder); //
System.out.println("SHA 암호화: " + passwordEncoding.encode(password));
System.out.println("SHA 비교: " + passwordEncoding.matches(password, passwordEncoding.encode(password)));
//BCryptPasswordEncoder 여기서 PasswordEncoding 상속 받았다.
//스프링 시큐리티에서 기본적을 사용하는 암호화 방식으로 암호화가 될때마다 새로운 값을 생성한다.
//임의적인 값을 추가해서 암호화하지 않아도 된다. (salt 사용하지 않는다.)
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
passwordEncoding = new PasswordEncoding(passwordEncoder);
//두번째 암호화 생성 암호 first2
String first2=passwordEncoding.encode(password);
System.out.println("1. BCrypt 암호화: " + passwordEncoding.encode(password));
System.out.println("1. BCrypt 비교: " + passwordEncoding.matches(password, passwordEncoding.encode(password)));
// 3번째 다시 BCrypt 암호화 암호 first3 최종적으로 DB 에 저장 될 값 은 first3
String first3 =passwordEncoding.encode(first2);
System.out.println("2. BCrypt 암호화: " + passwordEncoding.encode(password));
System.out.println("2. BCrypt 비교: " + passwordEncoding.matches(password, passwordEncoding.encode(password)));
// **********************************************************************************
//DB에 저장 된 값 1234 의 암호화
//로기인 유저 암호화
//DB에 저장된 first3 값과 로그할 유저가 넣는 1234 값 비교
System.out.println(" 반환 값 :" + dbPassword("1234"));
System.out.println("로그인 확인: " + passwordEncoding.matches("1234", dbPassword("1234")) );
}
//DB 에 변경해서 저장될 암호
public String dbPassword(String loginPassword){
SHAPasswordEncoder shaPasswordEncoder = new SHAPasswordEncoder(512);
shaPasswordEncoder.setEncodeHashAsBase64(true);
PasswordEncoding passwordEncoding = new PasswordEncoding(shaPasswordEncoder);
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
passwordEncoding = new PasswordEncoding(passwordEncoder);
//세번째 암호화
String passwod =passwordEncoding.encode(loginPassword);
return passwod;
}
}
MYSQL 추가 DB에서 추가 암호 작업을 해보자
PASSWORD 함수도 있지만
mysql 에는 데이터를 암호화, 복호화하는 AES_ENCRYPT, AES_DECRYPT 함수가 있습니다. 이 암호화는 128비트 길이로 인코딩되어 저장됩니다. 그러나 소스를 수정해서 256비트까지 확장시킬수 있다고 합니다.
AES_ENCRYPT 는 문자열을 암호화하고, 바이너리 문자열을 반환하지만, AES_DECRYPT 는 암호화된 문자열을 복호화합니다. AES_DECRYPT 는 유효하지 않은 데이터는 padding을 감지하고, NULL을 반환합니다.
# "암호화 키"는 임의의 값이 올 수 있으며, "문자열"은 암호화하고자 하는 값이 됩니다. # AES_ENCRYPT 암호화 INSERT INTO 테이블명 VALUES (HEX(AES_ENCRYPT('문자열', '암호화 키'))); # AES_DECRYPT 복호화 SELECT AES_DECRYPT(UNHEX(필드명), '암호화 키') FROM 테이블명; |
예제 (ex #1
# AES_ENCRYPT 암호화 INSERT INTO tbname VALUE (HEX(AES_ENCRYPT('123456','가나다라'))); // 결과: 5A33E11DC0B638E4E5E74EBD52F55E3D # AES_DECRYPT 복호화 SELECT AES_DECRYPT(UNHEX(필드명), '가나다라') FROM tbname; |
이러한 방법으로
다시 한번 2중으로 DB에서 암호를 해 봅시다.
컨트롤
비밀번호 1111 값으로 스프링으로 암호를 하였더니
값 이 : $2a$10$MZSbH9WT5QdBqP1BG5mp7ud0v8JWAYre/1OyvH5voB7RTcjbzH/.e 였다.
이것을 MYSQL 에서 다시 HEX(AES_ENCRYPT('$2a$10$MZSbH9WT5QdBqP1BG5mp7ud0v8JWAYre/1OyvH5voB7RTcjbzH/.e','java$$##1'))
으로 키값을 java$$##1 을 주고 암호를 해서 DB에 저장을 하였다.
INSERT INTO tbl_member (userid, userpw)
value ('admin' , HEX(AES_ENCRYPT('$2a$10$MZSbH9WT5QdBqP1BG5mp7ud0v8JWAYre/1OyvH5voB7RTcjbzH/.e','java$$##1')));
DB에 저장 된 값이 :
C7487C65CA7CC192AA636F828E78ADF36C95E96B0435401900AF5420F2AA4FEF0B971DC606FDC4E4491EE6EAE429CC3E27CF0B6CD4CB626A18C3EBAC8CF8CDEF
으로 나왔다. 1111 네자리 비밀번호가 130자리 수로 암호화 되어 저장 되었다.
@Inject
private PasswordEncoding passwordEncoding;
@RequestMapping(value="/login", method=RequestMethod.POST)
public String login2(String userid, String userpw, HttpServletRequest request, RedirectAttributes rttr){
try{
//DB에서 가져온 패스워드
String dbPw=memberDAO.loginPasswd(userid);
//내가 입력한 input 패스워드 와 매치 해서 비교
//true 일치.
logger.info("DB에서 가져온 dbpw 값: " +dbPw );
if(passwordEncoding.matches(userpw, dbPw)){
HttpSession session =request.getSession(false);
MemberDTO dto =new MemberDTO();
dto.setUserid(userid);
dto.setUsername("홍길동");
session.setAttribute("loginUser", dto);
return "redirect:/";
}else{
rttr.addFlashAttribute("loginError", "아이디 또는 패스워드가 일치하지 않습니다.");
return "redirect:login";
}
}catch(Exception e){
logger.info(e.getMessage());
return "redirect:login";
}
}
//DB에 있는 유저 아이디 와 DB에 저장 된 패스워드 가져오기
@Override
public String loginPasswd(String userid) {
return sqlSession.selectOne(namespsce+".loginPasswd", userid);
}
<!-- # AES_DECRYPT 복호화
?SELECT AES_DECRYPT(UNHEX(필드명), '가나다라') FROM tbname; -->
<select id="loginPasswd" resultType="string">
select AES_DECRYPT(UNHEX(userpw), 'java$$##1') from tbl_member where userid=#{userid}
</select>
댓글 ( 8)
댓글 남기기