728x90
300x250

[Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (2)


이번에는 이전 글에 이어서 Spring-Security를 구현해보려고 한다.

조금 이번 글부터는 난이도가 있어지니깐 개발 전략을 잘 숙지해서 작업하면 좋겠다.


1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1), 2020-09-27

https://yyman.tistory.com/1422



14. 개발 전략


하나 만들면 다 되는 게 아니다. 계속 연속해서 복합적으로 수정작업을 해줘야 한다.

그래서 개발 작업이 힘이 든다. 쉽지만 않다.


그림 24. 개발 전략도


SecurityWebApplicationInitializer.java, SecurityConfig.java는 Spring Security 구현에 있어서 핵심이라고 해도 무방하다.

두 개를 잘 구현한다면, 셈플 로그인 페이지는 볼 수 있다.


문제는 자바 버전으로 구현했을 때 보안 토큰 절차가 xml방식에 비해서 매우 까다롭게 반응한다는 것이다.

그래서 간단한 코드로 태스트를 해보기도 전에 DB 설계를 할 수 밖에 없었다.


이유는 토큰 인증 때문에 그렇다.


SqlMapSessionFactory도 계속 반복해서 다양한 영역에서 재사용될 것이다.



15. config의 SecurityWebApplicationInitializer.java (필수 파일)


이 파일을 보면, 제일 황당한 생각이 들 수 밖에 없는 이유가 코드는 몇 줄 안 되는데, 없으면 동작이 안 된다는 것이다.


package com.springMVC.javaSecurity5.config;


import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;


public class SecurityWebApplicationInitializer 

                        extends AbstractSecurityWebApplicationInitializer {


}


파일명: SecurityWebApplicationInitializer.java


[첨부(Attachments)]

SecurityWebApplicationInitializer.zip



16. config의 SecurityConfig.java (필수 파일)


"SecurityConfig.java" 이 파일도 없으면 Spring Security with 자바 버전이 동작되지 않는다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import javax.sql.DataSource;


import org.springframework.beans.factory.annotation.Autowired;

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.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import org.springframework.security.web.csrf.CsrfFilter;

import org.springframework.web.filter.CharacterEncodingFilter;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

 

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Autowired

    PasswordEncoder passwordEncoder;

    

    @Autowired

    private CustomAuthenticationProvider authProvider;

    

    protected void configure(AuthenticationManagerBuilder auth, HttpSecurity http) throws Exception {

      

    CharacterEncodingFilter filter = new CharacterEncodingFilter();


    /* UTF-8 한글 보완 */

        filter.setEncoding("UTF-8");

        filter.setForceEncoding(true);

        http.addFilterBefore(filter,CsrfFilter.class);

   

        auth

        .authenticationProvider(authProvider);

        

    /* 현재 - 임시

        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

         */

        

        // withUser("admin").password(passwordEncoder.encode("1234")).roles("USER", "ADMIN");

      

        /* 임시

   

    UserBuilder users = User.withDefaultPasswordEncoder();

      auth.inMemoryAuthentication()

        .withUser(users.username("admin").password("1234").roles("USER"))

        .withUser(users.username("user").password("1234").roles("ADMIN"));

    */

    }

 

 

    @Override

    protected void configure(HttpSecurity http) throws Exception {


        http.authorizeRequests()

        

        // index

        .antMatchers("/")

            .permitAll()


         // 접근 오류

     .antMatchers("/member/accessDenied")

         .permitAll()

         

         .antMatchers("/member/accessDeniedView")

         .permitAll()    


         // 회원 로그인 기능

     .antMatchers("/member/loginForm")

         .permitAll()

         

     // 관리자 페이지 기능

        .antMatchers("/admin/**")

        .hasRole("ADMIN")

        // "RULE_ADMIN이라고 DB에 입력되어 있다면, RULE_은 제거하고 입력해야 인식함."

        

        // 폼 로그인 명세

        .and()

            .formLogin()

        .permitAll()

                .loginPage("/member/loginForm")

                .failureForwardUrl("/member/loginForm?error")

                .defaultSuccessUrl("/")

                .usernameParameter("id")

                .passwordParameter("password")

       

        // 로그아웃 처리

.and()

                .logout()

                .logoutUrl("/logout")

                .logoutSuccessUrl("/")

                .invalidateHttpSession(true)

                .deleteCookies("JSESSION_ID")

                .deleteCookies("remember-me")

        // 로그인 

            .and()

            .rememberMe()

            .tokenValiditySeconds(604800)

            .tokenRepository(persistentTokenRepository())

            // 예외처리(

            .and()

        .exceptionHandling()

        .accessDeniedPage("/member/accessDenied")

        // csrf 설정

            .and()

                .csrf().disable();

       

        

            

        

    /*

        http.authorizeRequests()

        .antMatchers("/login")

            .permitAll()

        .antMatchers("/**")

            .hasAnyRole("ADMIN", "USER")

            .hasAnyAuthority("RULE_ADMIN", "RULE_USER")

        .and()

            .formLogin()

            .loginPage("/login")

            .defaultSuccessUrl("/")

            .failureUrl("/login?error=true")

            .permitAll()

            

            .loginPage("/login")

            .usernameParameter("email")

            .passwordParameter("password")

            .successHandler(successHandler())      

            .failureHandler(failureHandler())

            .permitAll();

            

        .and()

            .logout()

            .logoutSuccessUrl("/login?logout=true")

            .invalidateHttpSession(true)

            .permitAll()

        .and()

            .csrf()

            .disable();

    */

        

    }

    

    // 로그아웃 Persistent_Logins에 관한 설정   (주석 해도 무방)...

    @Bean 

public PersistentTokenRepository persistentTokenRepository() {

JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();

DataSource usrDS = getDataSource();

db.setDataSource(usrDS);

return db;

}

    


    // DataSource 불러오기    (주석 해도 무방)

@Bean

public DataSource getDataSource() {

       // BasicDataSource dataSource = new BasicDataSource(); - Apache DBCP2

SqlMapSessionFactory factory = SqlMapSessionFactory.getInstance();

       return factory.getOracleDataSource(); // 오라클 적용함.

}

    

// 비밀번호 생성 - 암호(BCryptPasswordEncoder)

    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

        

    }

    

}


파일명: SecurityConfig.java


[첨부(Attachments)]

SecurityConfig.zip



비고: 주석 잘 쳐서 정리해서 빌드해보면, 내장 로그인 페이지를 볼 수 있다.
       - SecurityWebApplicationInitializer.java, SecurityConfig.java 두 개 파일의 힘이 얼마나 큰지 실감 해볼 수 있다.

         Spring-Framework 설정 전체를 제어해버린다고 해도 된다.




17. 로그인 인증 - Spring Security (CustomAuthenticationProvider.java)


이 코드 부분은 찾아보려고 해도 쉽게 나오지 않는다. 어려운 부분 중 하나이다.

공개하는 이유는 삽질을 적게 하라는 의미이다.


패키지 경로: com.springMVC.javaSecurity5.config


package com.springMVC.javaSecurity5.config;


import java.util.List;


import org.springframework.security.authentication.AuthenticationProvider;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.stereotype.Component;


import com.springMVC.javaSecurity5.service.CustomUserDetailsService;


@Component

public class CustomAuthenticationProvider implements AuthenticationProvider {

    

    private UserDetailsService userDeSer;

 

    @Override

    public Authentication authenticate(Authentication authentication) {

        

        String username = (String) authentication.getPrincipal();

        String password = (String) authentication.getCredentials();

        

        if ( username.equals("fail")) {

        System.out.println("(에러)아이디: 실패");

        return null;

        }

        

        // DB 정보 읽기

        userDeSer = new CustomUserDetailsService();

        UserDetails userDetail = userDeSer.loadUserByUsername(username);

        

        @SuppressWarnings("unchecked")

  List<GrantedAuthority> roles = (List<GrantedAuthority>) userDetail.getAuthorities();

        

        // 권한

        System.out.println("DB불러오기-권한:" + userDetail.getAuthorities());

        System.out.println("DB불러오기-비밀번호:" + userDetail.getPassword());

        System.out.println("roles:" + roles.get(0));

        

        if ( !matchPassword(password, userDetail.getPassword())) {

        System.out.println("(에러)비밀번호: 불일치" + password);

        return null;

        }

        

        UsernamePasswordAuthenticationToken result =

        new UsernamePasswordAuthenticationToken(username, password, roles);

        

        result.setDetails(userDetail);

        

        return result;

    }

 

    @Override

    public boolean supports(Class<?> authentication) {

        return true;

    }

    

    private boolean matchPassword(String loginPwd, String password) {

   

        BCryptPasswordEncoder secure = new BCryptPasswordEncoder();

        return secure.matches(loginPwd, password);

    }

 

}


파일명: CustomAuthenicationProvider.java


[첨부(Attachments)]

CustomAuthenticationProvider.zip


어디에 구체적으로 사용되는 부분인가?


사용하는 영역은 SecurityConfig.java에 http의 auth의 .authenicationProvider()에 사용된다.



그림 25. SecurityConfig.java


왜 이 코드를 사용하는지 소개해본다면,

"No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken"

이 문제가 디버그 오류창에 뜨는 것을 볼 수 있다.


기본 내장형 계정 생성 등으로 인증을 시도하면 xml방식에서는 처리해줬는데, java방식에서는 인증받지 못한다.


     /* 현재 - 임시


        auth.inMemoryAuthentication()

        .passwordEncoder(passwordEncoder)

        .withUser("user").password(passwordEncoder.encode("1234")).roles("RULE_USER")

        .and()

        .withUser("admin").password(passwordEncoder.encode("1234")).roles("RULE_USER", "RULE_ADMIN");

        

        */


[문제가 발생되는 기본형 - 코드]


이 코드로 작업하면, xml에서는 동작되었던 부분이 동작되질 않는다.


토큰 인증도 해결할 겸 "CustomAuthenicationProvider.java를 설계해서 문제를 해결한 것이다.




18. SQL (Factory) - SqlMapSessionFactory.java


나중에 CP(Connection Pool, 커넥션 풀)이라고 불리는 것으로 구현해봐도 좋을 듯 싶다.

iBatis를 남용해서 프로젝트에 기본마냥 소개하는 책들이 무척 많은데 기본은 순수한 DB를 사용하는 것부터 출발하는 것이다.


iBatis는 SQL 코드 개발 등에서 생산성이 좋아지는 도구 중 하나이지만, 필수 사항은 아니라고 본다.

차라리 필수 사항을 꼽아본다면, 커넥션 풀을 하나 추천해보고 싶다.


아무튼 커넥션 풀 주제가 아니기 때문에 생략한다.


패키지 경로: com.springMVC.javaSecurity5.db


package com.springMVC.javaSecurity5.db;


import java.io.IOException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.Properties;


import javax.sql.DataSource;


import oracle.jdbc.pool.OracleDataSource;


public class SqlMapSessionFactory {

private static SqlMapSessionFactory factory = new SqlMapSessionFactory();


private SqlMapSessionFactory() {}


private final String driverName = "oracle.jdbc.driver.OracleDriver";

private final String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";

private final String userName = "{사용자계정명}";

private final String userPassword = "{비밀번호}";

public static SqlMapSessionFactory getInstance() {

return factory;

}


/*

*     public static DataSource getMySQLDataSource() {

        Properties props = new Properties();

        FileInputStream fis = null;

        MysqlDataSource mysqlDS = null;

        try {

            fis = new FileInputStream("db.properties");

            props.load(fis);

            mysqlDS = new MysqlDataSource();

            mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));

            mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));

            mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));

        } catch (IOException e) {

            e.printStackTrace();

        }

        return mysqlDS;

    }

    */

    public DataSource getOracleDataSource(){

        

    OracleDataSource oracleDS = null;

        

    try {

            oracleDS = new OracleDataSource();

            oracleDS.setURL(dbUrl);

            oracleDS.setUser(userName);

            oracleDS.setPassword(userPassword);

        } catch (SQLException e) {

            e.printStackTrace();

        }

        return oracleDS;

        

    }

public Connection connect() {


Connection conn = null;


try {

Class.forName(driverName);

conn = DriverManager.getConnection(dbUrl, userName, userPassword);

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


return conn;


}


public void close(Connection conn, PreparedStatement ps, ResultSet rs) {


if ( rs != null ) {


try {

rs.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}


close(conn, ps); // Recursive 구조 응용(재귀 함수)


} // end of if


}


public void close(Connection conn, PreparedStatement ps) {


if (ps != null ) {

try {

ps.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


if (conn != null ) {

try {

conn.close();

}

catch(Exception ex) {

System.out.println("오류 발생: " + ex);

}

} // end of if


}

}



파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip



비고: 재사용이 가능한 형태로 설계하였다.



19. Model - CustomUserDetails.java


순수한 Model 형태는 아니고, 부분 개량하였다.

Spring-Security에서 제공하는 UserDetails(인터페이스)를 Model 클래스에 구현해야 한다.

List<role> 기능 문제 등으로 인해서 String authorities를 List<GrantedAuthority>로 변경하였다.


@Override 된 부분들이 UserDetails에 정의된 내용이다.


패키지 경로: com.springMVC.javaSecurity5.model


package com.springMVC.javaSecurity5.model;


import java.util.ArrayList;

import java.util.Collection;

import java.util.List;


import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;


public class CustomUserDetails implements UserDetails{


private static final long serialVersionUID = 1L;

private String username;

    private String password;

    // 개량함. (다중 권한 고려)

    private List<GrantedAuthority> authorities;

    private boolean enabled;

        

public String getUsername() {

return username;

}


public void setUsername(String username) {

this.username = username;

}


public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

     public void setAuthority(String authority) {

// 권한 객체 생성

if ( authorities == null ) {

authorities = new ArrayList<GrantedAuthority>();

}

// 권한 추가

SimpleGrantedAuthority grantObj = new SimpleGrantedAuthority(authority);

authorities.add(grantObj);

     }


public boolean getEnabled() {

return enabled;

}

public void setEnabled(boolean enabled) {

this.enabled = enabled;

}


@Override

public Collection<? extends GrantedAuthority> getAuthorities() {


        return authorities;


}


@Override

public boolean isAccountNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isAccountNonLocked() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isCredentialsNonExpired() {

// TODO Auto-generated method stub

return false;

}


@Override

public boolean isEnabled() {

// TODO Auto-generated method stub

return false;

}

    

}


파일명: CustomUserDetails.java


[첨부(Attachments)]

CustomUserDetails.zip





20. Service - CustomUserDetailsService.java


CustomUserDetailsService는 Spring-Security의 "UserDetailsService(인터페이스)"로 정의된 내용을 구현하는 것이다.

인터페이스의 영향도 있지만, DB를 실제로 불러올 때 사용자 관점에서 처리되는 부분이라고 본다.


package com.springMVC.javaSecurity5.service;


import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.core.userdetails.UsernameNotFoundException;


import com.springMVC.javaSecurity5.dao.SqlSessionTemplate;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class CustomUserDetailsService implements UserDetailsService {

    

    private SqlSessionTemplate sqlSession = SqlSessionTemplate.getInstance();

 

    public CustomUserDetails getUserById(String username) {

        return sqlSession.selectOne("user.selectUserById", username);

    }

 

    public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {


    CustomUserDetails user = sqlSession.selectOne("null", username);

        

        if(user==null) {

            throw new UsernameNotFoundException(username);

        }

        return user;

    }

    

}


파일명: CustomUserDetailsService.java


[첨부(Attachments)]

CustomUserDetailsService.zip




21. DAO - SqlSessionTemplate.java


실제 DB를 구현하는 부분이다.


package com.springMVC.javaSecurity5.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


import com.springMVC.javaSecurity5.db.SqlMapSessionFactory;

import com.springMVC.javaSecurity5.model.CustomUserDetails;


public class SqlSessionTemplate {


private SqlSessionTemplate() {}

private static SqlSessionTemplate sqlTemplate;

    private static SqlMapSessionFactory session; 

    

    public static SqlSessionTemplate getInstance(){

   

        if(sqlTemplate == null){

        sqlTemplate = new SqlSessionTemplate();

            session = SqlMapSessionFactory.getInstance();

        }


        return sqlTemplate;

    }

    

// 추후 iBatis 고려

public CustomUserDetails selectOne(String id, String username) {

Connection conn = null;

    PreparedStatement pstmt = null;

    ResultSet rs = null;

   

    CustomUserDetails node = null;


    String sql = "select g1.username, g1.password, g2.authority, " + 

       "g1.enabled from comp_users g1, comp_authorities g2 where g1.username = g2.username " +

       "and g1.username = ?";


    System.out.println(sql);


    try {


    conn = session.connect();


    pstmt = conn.prepareStatement(sql);

    pstmt.setString(1, username);

   

    rs = pstmt.executeQuery();


    while ( rs.next() ) {

   

    // 데이터가 존재할 때, 노드 생성

    node = new CustomUserDetails();

    node.setUsername(rs.getNString(1));

    node.setPassword(rs.getNString(2));

    node.setAuthority(rs.getNString(3));

    System.out.println("rs:" + rs.getNString(3));

    node.setEnabled(rs.getBoolean(4));

    }


    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    session.close(conn, pstmt, rs);

    }


    return node;

}

}



파일명: SqlSessionTemplate.java


[첨부(Attachments)]

SqlSessionTemplate.zip



* 3부에서는 View에 대해서 구현하는 방법을 소개하겠다.


3부에서는 "jsp 파일" 등 사용자 인터페이스 화면에 대해서 소개하겠다.


1. [Spring-Framework] 17. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (3), 2020-09-27

https://yyman.tistory.com/1424


반응형
728x90
300x250

[Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (1)


이번에 소개할 프로젝트는 13, 14, 15번 게시글을 Java 방식으로 구현한 프로젝트이다.

동일하게 진행할 것이다. 실질적으로 제대로 소개하고 있는 책이나 게시글은 찾아보질 못했다.

많은 오류가 있었다는 이야기이다.


순수한 Spring-Framework에서도 Spring Security를 원만히 잘 사용할 수 있도록 하는 게 글의 핵심이다.

이번 게시글도 양이 많아서 나눠서 작성하였으니 잘 따라해보면 도움이 될 것으로 보인다.



[작업환경]


[WAS(Web Application Server), 웹 애플리케이션 서버]

1. apache-tomcat-9.0.37-windows-x64


[DBMS(DataBase Management System - Tools]

2. sqldeveloper-19.2.1.247.2212-x64


[DB(DataBase)]

3. Oracle Databases 19.3.0.0


[IDE(Integration Development Environment)]

4. Spring-Tool Suites 4-4.7.2. Releases.


[Framework(프레임워크)]

5. spring-security-taglibs(5.4)

6. spring-security-config(5.4)

7. spring-security-web(5.4)

8. spring-security-core(5.4)

9. javax.servlet-api(4.0.1)

10. spring-webmvc(5.2.9.RELEASE)

11. spring-context(5.2.9.RELEASE)

12. Maven 3.6.3/1.16.0.20200610-1735


[Java]

OpenJDK-14.0.2 (https://openjdk.java.net/)



[이전 글 주제]

1. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (1)

https://yyman.tistory.com/1419

2. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (2)

https://yyman.tistory.com/1420

3. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (3)  // 자동 로그인 유지, 유지 세션

https://yyman.tistory.com/1421




1. XML 기반 프로젝트 vs 자바 기반 프로젝트


이번에 소개할 주제는 자바 기반 프로젝트이다.

이전 게시물과 지금 구현할 게시물의 차이점에 대해서 간단하게 소개하겠다.


 


그림 1. XML 방식


 


그림 2. 자바 방식(Java)




(특징)
- Spring 폴더가 있으며, xml 파일이 존재함.

- web.xml 파일이 있음.

- 기본 프로젝트 pom.xml을 별도로 설정하지 않아도 됨.

(특징)
- Spring 폴더가 없음. (있으면 충돌나서 실행이 안 됨.)

- web.xml 파일이 없음. 

- 기본 프로젝트 생성 후 pom.xml을 별도로 설정해야 함.

(구현 난이도) 무난함.

(셋팅 값 및 지정된 변수 등을 잘 사용하는 것이 매우 중요함)


- 적당한 지식이면 무난할 것으로 보임.

(구현 난이도) 머리를 조금 사용해야 함.
- 초기 구축을 놓고 보면,
  코드가 늘어나고 조금 복잡할 수 있음.
  (쉽지만 않다. API도 읽어내야 하고 많은 실험이 요구됨.)


- 많은 프로그래밍 지식이 다소 필요함.

사용해보면, 장/단점이 있음.

-> 간단하게 관리하는 면에서는 편할 수도 있음.

   (xml)

-> 튜닝에 있어서 제약이 있을 수도 있음.

......


[이전 - XML 기반 게시글]

1. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (1), 2020. 9. 26

https://yyman.tistory.com/1419


2. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (2), 2020. 9. 26

https://yyman.tistory.com/1420


3. [Spring-Framework] 13. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-XML) (3), 2020. 9. 26

https://yyman.tistory.com/1421



2. 결과(1) - (Result)


출력 결과는 이전 프로젝트(13, 14, 15번)과 동일하게 구현 하였다.



그림 3. 결과(자바 버전) - 메인



그림 4. 결과(자바 버전) - 로그인 폼


그림 5. 결과(자바 버전) - 로그인 실패



그림 6. 결과(자바 버전) - 로그인 후



그림 7. 결과(자바 버전) - 비밀번호 출력의 예(암호화 패키지) - 업데이트 전



그림 8. 결과(자바 버전) - 로그인 후 모습 - 업데이트 전




그림 9. 결과(자바 버전) - 권한이 없는 계정이 특정 페이지를 접속할 때 모습(1) // 순정 (업데이트 전)



그림 9-1. 결과(자바 버전) - 권한이 없는 계정이 특정 페이지를 접속할 때 모습(2) // 커스텀 (업데이트 완료함)



그림 9-2. 결과(자바 버전) - 권한이 있는 계정의 메뉴 모습 // 커스텀 (업데이트 함)



그림 9-3. 결과(자바 버전) - 권한이 없는 계정의 메뉴 모습 // 커스텀 (업데이트 함)






3. 결과(2) - 프로젝트 구성


프로젝트 구성에 대한 것이다.

상당히 양이 많다. 초기 셋팅만 잘 해주면, 나머지는 순조로울 것으로 보인다.



그림 10, 11. 프로젝트 구성


다소 구현할 양이 많다는 것을 확인할 수 있다.


완성된 프로젝트 구성에 대해서 살펴보는 것은 무엇을 진행할 것인지 예상할 수 있는 방법 중 하나라고 주장해본다.



4. 데이터베이스 설계


크게 데이터베이스는 공식적으로 설계하라고 제시하는 규격 테이블 1개와 사용자 정의 테이블 5개로 총 6개의 테이블을 작성해야 한다.
아마 이런 부분은 시중 책이나 강의 등에서는 다뤄보지 못한 부분일 수도 있다.
권한이 있는 계정을 그룹별 권한, 페이지별 권한으로 체계화시킨 설계도이다.

Spring Security에서 요구하는 기능을 기본적으로 담은 설계이니 활용하면 참고하면 도움이 될 것으로 보인다.



그림 12. ER-D


테이블을 작성하는 데, 순번을 대략적으로 잡아본 이유는 외래키, 참조키 등에 기준이 되는 테이블을 먼저 생성하고 작업하는 것이 좋기 때문이다.

편집해서 외래키, 참조키 등 지정해줘도 무방하다. 단, 무결성이나 각종 조건 등 때문에 데이터가 존재하지 않은 상태에서 진행해야 한다.



그림 13. 테이블: COMP_GROUP



그림 14. 테이블: COMP_USERS



그림 15. 테이블: COMP_GROUP_AUTHORITIES



그림 16. 테이블: COMP_GROUP_MEMBERS



그림 17. 테이블: COMP_AUTHORITIES




그림 18. 테이블: PERSISTENT_LOGINS




5. 테이블 - SQL(Create table - DML)


테이블 작성에 관한 사항이다.


CREATE TABLE comp_users (

username VARCHAR(50) NOT NULL,

password VARCHAR(300) NOT NULL,

enabled INT NOT NULL,

PRIMARY KEY (username)

);


CREATE TABLE comp_authorities (

  username VARCHAR(50) NOT NULL,

  authority VARCHAR(50) NOT NULL,

  CONSTRAINT fk_authorities_users FOREIGN KEY (username) REFERENCES comp_users (username)

); 


CREATE TABLE comp_groups(

id VARCHAR2(20) NOT NULL,

group_name VARCHAR2(20) NULL

);


CREATE TABLE comp_group_authorities(

group_id VARCHAR2(20) NOT NULL,

authority VARCHAR2(20) NOT NULL

);


CREATE TABLE comp_group_members(

group_id VARCHAR2(20) NOT NULL,

username VARCHAR2(20) NOT NULL

);


CREATE TABLE persistent_logins (

username VARCHAR(64) NOT NULL,

series VARCHAR(64) PRIMARY KEY,

token VARCHAR(64) NOT NULL,

last_used TIMESTAMP NOT NULL

);



-- 계정

INSERT INTO comp_users (username, password, enabled) VALUES ('user', '$2a$10$x04djNV2e9rpcPPRyXoLk.rMm6iZe2/vYdzpqHQcLeNSYdt7kc30O', 1);

INSERT INTO comp_users (username, password, enabled) VALUES ('admin', '$2a$10$QUddY3O/6ZgkYCR6MFlv9.nqA501Fm0cc/ZxQHX5pwb1o0CYCTiIS', 1);


-- 사용자 권한

INSERT INTO comp_authorities (username, authority) VALUES ('user', 'ROLE_ADMIN');

INSERT INTO comp_authorities (username, authority) VALUES ('admin', 'ROLE_USER');


-- 그룹

INSERT INTO comp_groups (id, group_name) VALUES ('G01', '관리자 그룹');

INSERT INTO comp_groups (id, group_name) VALUES ('G02', '사용자 그룹');


-- 그룹 권한

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G01', 'ROLE_ADMIN');

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G01', 'ROLE_USER');

INSERT INTO comp_group_authorities (group_id, authority) VALUES ('G02', 'ROLE_USER');


-- 그룹 회원

INSERT INTO comp_group_members (group_id, username) VALUES ('G01', 'user');

INSERT INTO comp_group_members (group_id, username) VALUES ('G02', 'admin');



파일명: sampleDb-oracledb.sql


[첨부(Attachments)]

sampleDb-oracledb.zip



비고: 꼭 오라클만 되는 것은 아니고, 살짝 튜닝하면 MySQL 등에서도 사용할 수 있는 형태로 작성하였다.

(SQL 명령어가 일부 데이터베이스에서는 차이가 있을 수도 있음. 특정 DB의 함수 등)



6. 프로젝트 - 신규 생성하기


Spring-Legacy-Project를 생성한다.



그림 19. 테이블: Spring MVC Project 생성하기


Spring MVC Project를 선택한다.

Project Name의 항목을 입력한다.

Next를 누른다.




그림 20. 테이블: Spring MVC Project 생성하기


top-level-package를 입력한 후 Finish를 누른다.




7. 프로젝트 - pom.xml 수정작업


양이 많긴 하지만, 작성해본다.


<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.springMVC</groupId>

<artifactId>javaSecurity5</artifactId>

<name>SpringSecurity5-Java</name>

<packaging>war</packaging>

<version>1.0.0-BUILD-SNAPSHOT</version>

<properties>

<!-- web.xml 사용 안함 표기 -->

<failOnMissingWebXml>false</failOnMissingWebXml>

<java-version>14</java-version>

<org.springframework-version>5.2.9.RELEASE</org.springframework-version>

<org.aspectj-version>1.6.10</org.aspectj-version>

<org.slf4j-version>1.6.6</org.slf4j-version>

</properties>

<dependencies>

<!-- Spring -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${org.springframework-version}</version>

<exclusions>

<!-- Exclude Commons Logging in favor of SLF4j -->

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${org.springframework-version}</version>

</dependency>

<!-- AspectJ -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>${org.aspectj-version}</version>

</dependency>

<!-- Logging -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${org.slf4j-version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.15</version>

<exclusions>

<exclusion>

<groupId>javax.mail</groupId>

<artifactId>mail</artifactId>

</exclusion>

<exclusion>

<groupId>javax.jms</groupId>

<artifactId>jms</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jdmk</groupId>

<artifactId>jmxtools</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jmx</groupId>

<artifactId>jmxri</artifactId>

</exclusion>

</exclusions>

<scope>runtime</scope>

</dependency>


<!-- @Inject -->

<dependency>

<groupId>javax.inject</groupId>

<artifactId>javax.inject</artifactId>

<version>1</version>

</dependency>

<!-- Servlet -->

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet.jsp</groupId>

<artifactId>jsp-api</artifactId>

<version>2.1</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

<version>1.2</version>

</dependency>

<!-- Test -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-core</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-web</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-config</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->

<dependency>

    <groupId>org.springframework.security</groupId>

    <artifactId>spring-security-taglibs</artifactId>

    <version>5.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->

<dependency>

    <groupId>javax.validation</groupId>

    <artifactId>validation-api</artifactId>

    <version>2.0.1.Final</version>

</dependency>


<dependency>

<groupId>com.oracle.database.jdbc</groupId>

<artifactId>ojdbc8</artifactId>

<version>19.7.0.0</version>

</dependency>

</dependencies>

    <build>

        <plugins>

            <plugin>

                <artifactId>maven-eclipse-plugin</artifactId>

                <version>2.9</version>

                <configuration>

                    <additionalProjectnatures>

                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>

                    </additionalProjectnatures>

                    <additionalBuildcommands>

                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>

                    </additionalBuildcommands>

                    <downloadSources>true</downloadSources>

                    <downloadJavadocs>true</downloadJavadocs>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>2.5.1</version>

                <configuration>

                    <source>1.6</source>

                    <target>1.6</target>

                    <compilerArgument>-Xlint:all</compilerArgument>

                    <showWarnings>true</showWarnings>

                    <showDeprecation>true</showDeprecation>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.codehaus.mojo</groupId>

                <artifactId>exec-maven-plugin</artifactId>

                <version>1.2.1</version>

                <configuration>

                    <mainClass>org.test.int1.Main</mainClass>

                </configuration>

            </plugin>

        </plugins>

    </build>

</project>




- oracle jdbc를 못 찾는 사람들을 위해서, oracle jdbc라고 검색하거나 ojdbc8.jar 파일을 구해서 사용하면 된다.
- Oracle Databases를 설치해서 사용중인 경우에는 아래처럼 하면 사용할 수 있다.


그림 21. pom.xml 파일 선택 후 마우스 오른쪽 버튼 메뉴 모습


pom.xml을 선택한다.

마우스 오른쪽 버튼을 누른다.

maven-> Add Dependency를 클릭한다.



그림 22. Add Dependency에서 Oracle 검색하기


oracle을 검색한다.
com.oracle.database.jdbc  | ojdbc8을 선택한 후 OK를 누른다.






8. 프로젝트 - web.xml, spring 폴더 제거 작업


파일: /src/main/web-app/WEB-INF/web.xml   (삭제할 것)

폴더: /src/main/web-app/WEB-INF/spring      (삭제할 것)




9. 프로젝트 - Build Path, Project factes 버전 바꾸기


초기 Spring MVC Project를 생성하면 1.6버전으로 설정되어 있다. 14버전으로 바꿔주겠다.



그림 23. Properties 속성 바꿔주기(1) / Build-Path - JavaSE 14로


JRE System Library [JavaSE-14]로 Edit 버튼을 통해서 수정해준다.



그림 23. Properties 속성 바꿔주기(2) / Project Factes - JavaSE 14로


Java 버전을 1.6에서 14로 바꿔준다.




10. Controller - web.xml 제거 (핵심작업)


web.xml 제거한 Spring MVC라는 주제로 접근하여 작성하려고 한다.

지금 작업, pom.xml 수정 작업을 중심으로 HomeController.java(Servlet) 파일을 구성해서 view파일 jsp를 만들면 web.xml없이 동작하는 화면을 

볼 수 있다.


package com.springMVC.javaSecurity5.config;


import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;


public class SpringMvcAnnotation extends AbstractAnnotationConfigDispatcherServletInitializer {


  @Override

  protected Class<?>[] getRootConfigClasses() {

    return null;

  }


// bean 설정과 spring container 설정을 위한 Config 클래스를 등록한다.

// Config 클래스는 web.xml의 dispatcher servlet 초기화에 사용된 xml과 같은 기능을 한다.

  @Override

  protected Class<?>[] getServletConfigClasses() {

    return new Class[] { WebConfig.class };

  }


// web.xml의 servlet mapping 부분을 대체한다.

  @Override

  protected String[] getServletMappings() {

    return new String[] { "/" };

  }


}


파일명: SpringMvcAnnotation.java


[첨부(Attachments)]

SpringMvcAnnotation.zip




이 파일을 시작점으로 한다.


package com.springMVC.javaSecurity5.config;


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;

import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import org.springframework.web.servlet.view.InternalResourceViewResolver;

 

@EnableWebMvc // <mvc:annotation-driven>에 해당.

// @ComponentScan(basePackages = {"com.figo.web"})  // <context:component-scan base-package="”com.figo.web”/">에 해당됨.

@ComponentScan("com.springMVC.javaSecurity5")

@Configuration

public class WebConfig extends WebMvcConfigurerAdapter {

 

    // <resources mapping="/resources/**" location="/resources/">에 해당됨.

    @Override

    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/").setCachePeriod(31556926);

    }

 

    // <mvc:default-servlet-handler>에 해당됨.

    @Override

    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {

        configurer.enable();

    }   

     

    // web.xml에서 봤던 내용들임.

    @Bean

    public InternalResourceViewResolver getInternalResourceViewResolver() {

        InternalResourceViewResolver resolver = new InternalResourceViewResolver();

        resolver.setPrefix("/WEB-INF/views/");

        resolver.setSuffix(".jsp");

        return resolver;

    }

    

    /*

    @Override

    public void addViewControllers(ViewControllerRegistry registry) {

   

        // registry.addViewController("/web").setViewName("home");

        registry.addViewController("/").setViewName("home");

        

    }

    */

 

}


파일명: WebConfig.java


[첨부(Attachments)]

WebConfig.zip



참고: HomeController의 초기내용으로 두고 서버 상에서 실행시켜봤다면, 동작했을 것으로 보인다.
(10번까지 잘 따라왔으면 화면 출력을 볼 수 있음.)



11. Controller - HomeController.java


HomeController에 관한 내용이다. 자세히 보면, "13, 14, 15번"글하고 거의 동일하다는 것을 알 수 있다.

쉽게 이야기하면, web.xml 파일부터 xml로 구성된 환경설정 파일을 프로젝트 내에서 제거한 것이다.

그러니 Controller 등은 동일할 수 밖에 없다고 본다.



package com.springMVC.javaSecurity5.controller;


import java.security.Principal;

import java.text.DateFormat;

import java.util.Date;

import java.util.Locale;


import javax.servlet.http.HttpServletRequest;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


//@CrossOrigin(origins = "*", allowedHeaders = "*")

@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);


@RequestMapping("/")

public String home(Locale locale, Model model, Principal principal) {


logger.info("Welcome home! The client locale is {}.", locale);


Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

String username = null;

// Principal 예제

if (principal != null) {

username = principal.getName();

System.out.println("타입정보 : " + principal.getClass());

System.out.println("ID정보 : " + principal.getName());

}

model.addAttribute("username", username);

model.addAttribute("serverTime", formattedDate );


return "home";

}

@RequestMapping("/admin")

public String admin(Locale locale, Model model) {

return "admin";

}


@RequestMapping(value = "/encode-password", method = RequestMethod.GET)

public String passwordEncode(Locale locale, Model model, HttpServletRequest req) {


// 1. xml 방식에서 java 방식으로 전환

// web.xml 파일 제거로 인한 사용 불가(ServletConfig sc 연결됨)

// WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());

        // PasswordEncoder passwordEncoder = context.getBean(PasswordEncoder.class);


PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        String password = req.getParameter("password");

        String encode = passwordEncoder.encode(password);


        model.addAttribute("encode", encode);

        System.out.println(encode);

        

        return "home";

        

}


}


파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip





12. Controller - AdminController.java


관리자 페이지에 관한 내용이다.


package com.springMVC.javaSecurity5.controller;

import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

public class AdminController {


private static final Logger logger = LoggerFactory.getLogger(AdminController.class);


@RequestMapping(value = "/admin/home", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome - 관리자 페이지(Admin Home)!");


return "admin/home";

}


}



파일명: AdminController.java


[첨부(Attachments)]

AdminController.zip



13. Controller - MemberController.java


MemberController.java에 관한 내용이다.


package com.springMVC.javaSecurity5.controller;


import java.util.Locale;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

public class MemberController {


// 커스텀 페이지 - 양식만

private static final Logger logger = LoggerFactory.getLogger(MemberController.class);


@RequestMapping(value = "/member/loginForm")

public String loginForm(Locale locale, Model model) {

logger.info("안녕 - 로그인 폼(Hello - Login Form");

// model.addAttribute("serverTime", formattedDate );

return "member/loginForm";

}


@RequestMapping(value = "/member/accessDenied")

public String accessDenied(Locale locale, Model model) {

logger.info("접근 금지 - 이동(Accessed Denied)");

// model.addAttribute("serverTime", formattedDate );

return "redirect:/member/accessDeniedView";

}

@RequestMapping(value = "/member/accessDeniedView")

public String accessDeniedView(Locale locale, Model model) {

logger.info("접근 금지 - 출력(Accessed Denied)");

// model.addAttribute("serverTime", formattedDate );

return "member/accessDenied";


}

}



파일명: MemberController.java


[첨부(Attachments)]

MemberController.zip




* 2부에서는


2부에서는 Spring-Security 설정에 대해서 집중적으로 다뤄보도록 하겠다.

조금 어려울 수도 있으니 마음을 편안하게 하고 따라해보면 좋겠다.


1. [Spring-Framework] 16. Spring MVC, Spring Security 5.4, Oracle - 보안처리(로그인-Java) (2), 2020-09-27

https://yyman.tistory.com/1423


반응형
728x90
300x250

[JSP] 16. 쿠키(Cookie) - 프로젝트(생성, 조회, 삭제)


쿠키에 대해서 소개하려고 한다.

쿠키라는 부분은 조작을 해버리면, 보안 자체가 매우 취약해서 사용 안 하는 것을 권장한다.

물론 전혀 사용 안 하는 것은 아니다.


쿠키는 서버 측으로부터 세션을 클라이언트로부터 내려받아서 세션 유지를 처리해주는 기술 중 하나이다.

서버와의 연결이 끊어져도 쿠키를 통해서 정보를 유지할 수 있다.


인터넷 검색을 시도 해보면, "특정 쿠키 변경"도 종종 검색되긴 하지만, 수 차례 태스트 결과로는 변경 작업은 시도 할 수 없다.

시도하려면, 쿠키를 전체 제거하고 다시 생성하는 방법 말고는 딱히 없다고 본다.



1. Cookie란?


쿠키란, 서버가 클라이언트에 저장하는 정보로써 클라이언트 쪽에 필요한 정보를 저장해놓고 필요할 때 추출하는 것을 지원하는 기술이다.


쿠키는 간단하게 구현할 수 있어서 "사용자 인증", "쇼핑몰 구축" 등에도 사용할 수 있지만, 브라우저에 저장되는 한계, 클라이언트가 직접 쿠키를 조작할 수 있기 때문에 보안에 취약해지는 단점이 있다.


- Cookie 생성 = new Cookie(String name, String value)

- 유효시간 설정 = setMaxAge(int expiry) 

- 쿠키 경로 설정 = setPath(String uri)

- 쿠키 도메인 설정 = setDomain(String domain)

- 쿠키 전송 = addCookie(Cookie cookie)


...... 쿠키는 직접 코드를 보는 게 훨씬 이해되는 데 도움된다고 주장한다.



2. Project 생성하기


이번 프로젝트는 꼭 반드시 Maven Project로 생성해야만 동작하는 건 아니다.

pom.xml을 사용하면, 라이브러리 관리가 수월해져서 그렇다.



그림 1. Maven Project 생성하기


org.apache.maven.archetypes  |   maven-archetype-webapp를 선택한 후 Next를 누른다.




그림 2. Maven Project 생성하기


Group ID와 Artifact Id를 입력한다.

Finish를 누른다.



3. pom.xml - 설정하기


이 글에서는 javax.servlet만을 추가하였다.



그림 3. Maven Project 생성하기




4. Servlet 생성하기


몇 가지 Servlet을 생성한다.



Java Resources에서 오른쪽 버튼을 누른다.

New->Servlet을 클릭한다.



그림 4. Servlet 생성 방법1


Java package명과 Class name명을 입력한다.

Finish를 누른다.




그림 5. Servlet 생성 방법2~4(반복)


그림 4의 동일한 방법으로 생성해준다.



5. web.xml - 설정하기


크게 많은 것을 작성한 건 아니다.

servlet 부분은 자동생성된 부분이다.

web-app, init-param 등 몇 가지를 수정, 추가하였다.


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">

 

  <display-name>Archetype Created Web Application</display-name>

  <welcome-file-list>

    <welcome-file>index.html</welcome-file>

    <welcome-file>index.htm</welcome-file>

    <welcome-file>index.do</welcome-file>

    <welcome-file>default.html</welcome-file>

    <welcome-file>default.htm</welcome-file>

    <welcome-file>default.do</welcome-file>

  </welcome-file-list>

  

  <servlet>

  <servlet-name>CookieTest1Servlet</servlet-name>

  <servlet-class>com.mavenCookie.web.CookieTest1Servlet</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet>

  <servlet-name>CookieExtract</servlet-name>

  <servlet-class>com.mavenCookie.web.CookieExtract</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet>

  <servlet-name>CookieAllRemove</servlet-name>

  <servlet-class>com.mavenCookie.web.CookieAllRemove</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet>

  <servlet-name>CookieModify</servlet-name>

  <servlet-class>com.mavenCookie.web.CookieModify</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  

  <servlet-mapping>

  <servlet-name>CookieTest1Servlet</servlet-name>

  <url-pattern>/CookieTest1Servlet</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>CookieExtract</servlet-name>

  <url-pattern>/CookieExtract</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>CookieAllRemove</servlet-name>

  <url-pattern>/CookieAllRemove</url-pattern>

  </servlet-mapping>

  <servlet-mapping>

  <servlet-name>CookieModify</servlet-name>

  <url-pattern>/CookieModify</url-pattern>

  </servlet-mapping>

</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip




6. index.jsp


index.jsp 파일이다.

확인 기능을 손쉽게 할 수 있도록 구현하였다.


<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8"></meta>

<title>Cookie 태스트</title>

<style>

a{

text-decoration:none;

}

</style>

</head>

<body>


<h2>Hello World!</h2>


<h3>이클립스 웹에서는 확인 불가</h3>

<a href="CookieTest1Servlet">쿠키 생성</a><br/>

<a href="CookieExtract">쿠키 추출</a><br/>

<a href="CookieAllRemove">쿠키 전체 삭제</a><br/>

<a href="CookieModify">쿠키 변경(결론: 불가능)</a><br/>

<div style="width:400px; height:200px; background-color:#e2e2e2">

<h5>참고로 웹 브라우저에서 쿠키를 제거하는 옵션을 켜버리면 유지 자체에 의미가 없어짐.</h5>


</div>

</body>

</html>



파일명: insert.jsp


[첨부(Attachments)]

index.zip




7. Servlet - CookieTest1Servlet


쿠키 생성 방법에 대한 소스이다.


package com.mavenCookie.web;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class CookieTest1Servlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    public CookieTest1Servlet() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

String charset = sc.getInitParameter("charset");

res.setContentType("text/html;charset=" + charset);

PrintWriter out = res.getWriter();

// 1. 쿠키 생성 - 기본 시간(브라우저 종료시 전송받은 쿠키 사라짐)

Cookie c1 = new Cookie("id", "userID");

c1.setPath("/");

res.addCookie(c1);

// 2. 쿠키 생성 - 쿠키 3시간 설정(60sec * 60Min * 3Hour)

Cookie c2 = new Cookie("mymy", "1234");

c2.setMaxAge(60 * 60 * 3);

c2.setPath("/");

res.addCookie(c2);

// 3. 쿠키 생성 - 쿠키 5일 설정(60sec * 60Min * 24Hour * 5Day)

Cookie c3 = new Cookie("subject", "zaza");

c3.setMaxAge(60 * 60 * 24 * 5);

c3.setPath("/"); // 경로는 루트로 지정함.

res.addCookie(c3);

out.println("쿠키 전송 완료<br/>");

out.println("<a href=\"index.jsp\">이전</a>");

out.close();

}


protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

}


}



파일명: CookieTest1Servlet.java


[첨부(Attachments)]

CookieTest1Servlet.zip




8. Servlet - CookieExtract


쿠키 내용을 조회하는 방법에 관한 것이다.


package com.mavenCookie.web;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class CookieExtract extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    public CookieExtract() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

String charset = sc.getInitParameter("charset");

res.setContentType("text/html;charset=" + charset);

PrintWriter out = res.getWriter();

Cookie[] list = req.getCookies();


// 삭제 여부 - 판별

if ( list != null ) {

out.print("유지 상태<br/>");

}else

{

out.print("삭제 완료<br/>");

}

// 쿠키 찾기(seq방식)

out.println("쿠키 검색1<br/>");

for ( int i = 0; list != null && i < list.length; i++ ) {

out.println( list[i].getName() + ":value=" + list[i].getValue() + "<br/>" );

}

// for 문 - 응용2

out.println("쿠키 검색2<br/>");

int i = 0;

for( Cookie cookie : list) {

out.println( list[i].getName() + ":value=" + list[i].getValue() + "<br/>" );

i++;

}

out.println("<a href=\"index.jsp\">이전</a>");

out.close();

}


protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

}


}



파일명: CookieExtract.java


[첨부(Attachments)]

CookieExtract.zip



9. Servlet - CookieAllRemove


쿠키를 전체 삭제하는 방법이다.


package com.mavenCookie.web;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class CookieAllRemove extends HttpServlet {

private static final long serialVersionUID = 1L;


    public CookieAllRemove() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

ServletConfig sc = this.getServletConfig();

String charset = sc.getInitParameter("charset");

res.setContentType("text/html;charset=" + charset);

PrintWriter out = res.getWriter();

Cookie[] list = req.getCookies();

for ( int i = 0; list != null && i < list.length; i++ ) {

// 만료처리 전

out.print( "이전 list[" + i + "]: " + list[i].getName() );

out.print( "<br/>" );

// 유효시간 0으로 설정하면 만료처리됨.

Cookie kc = new Cookie(list[i].getName(), null) ;

    kc.setMaxAge(0) ;

    kc.setPath("/");


// 응답에 쿠키 추가

res.addCookie(kc);

kc = null;

}

out.println("<br/>");

out.println("<a href=\"index.jsp\">이전</a>");

out.close();

}


protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

}


}



파일명: CookieAllRemove.java


[첨부(Attachments)]

CookieAllRemove.zip



10. Servlet - CookieModify(안 되는 코드)


안 된다고 표기를 해둔 이유는 setValue 등으로 수정 후 넘기면 될 것 같지만, 전혀 되지 않는다.


package com.mavenCookie.web;


import java.io.IOException;

import java.io.PrintWriter;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


/**

 * Servlet implementation class CookieModify

 */

public class CookieModify extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    public CookieModify() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

String charset = sc.getInitParameter("charset");

res.setContentType("text/html;charset=" + charset);

PrintWriter out = res.getWriter();

Cookie[] list = req.getCookies();

for ( Cookie cookie : list  ) {

if ( cookie.getName().equals("id")) {

cookie.setValue("haha");

res.addCookie(cookie);

}

}


out.println("<br/>");

out.println("<a href=\"index.jsp\">이전</a>");

out.close();

}


protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

}


}



파일명: CookieModify.java


[첨부(Attachments)]

CookieModify.zip



* 맺음글(Conclusion)


쿠키 프로젝트로 가볍게 쿠키 사용 방법에 대해서 살펴보았다.

반응형
728x90
300x250
[Sun Sys - Java] 자바 - Jar 파일 실행하기(윈도우, 리눅스)

 

자바 Jar 파일을 윈도우, 리눅스에서 실행하는 방법이다.

 

윈도우: javaw -jar client.jar

리눅스: java -jar client.jar

 

 

반응형
728x90
300x250
[JSP] 6. Apache Tomcat 9 기반으로 한 Eclipse에서 Servlet 사용하기(Using servlets in Eclipse based on Apache Tomcat 9)

 

Apache Tomcat 9와 Eclipse Java 2019-09를 활용한 Dynamic Web Project를 하나 진행하도록 하겠다.

해당 예제는 톰캣에 탑재되어 있는 기본 예제로 진행하였다.

(The Apache Tomcat 9 and the Eclipse Java 2019-09 A Dynamic Web Project will utilize the proceeds to one.

This example is a basic example of Tomcat.)

 


1. 실습 환경(Environmental Practice)

 

1. MS윈도우 10 1909(MS Windows 10 1909)

2. Apache Tomcat 9

3. Eclipse Java 2019-09

4. OpenJDK 13

 


2. 프로젝트 생성하기(Create a project)

 

프로젝트를 아래의 그림처럼 생성한다.(Create a project as shown below.)

 

 

 


3. Servlet 생성하기

 

서블랫에 대해서는 다음에 소개하도록 하겠다.

(I'll introduce you to the Servlet later.)

 

 

Java Resources 탭에서 src 폴더를 오른쪽 버튼을 클릭한다.

(Right-click the src folder on the Java Resources tab.)

 

 

그리고 New-> Servlet을 클릭한다.

(Then click New-> Servlet.)

 

 

 

 

 

 

 

 


4. Apache Tomcat 9의 Servlet API 찾기(Finding the Servlet API for Apache Tomcat 9)

 

서블랫의 경로는 아래의 그림처럼 존재한다.

C:\{Apache 설치경로}\lib\servlet-api.jar

(The servlet's path exists as shown below.

C:\{Apache installation path}\lib\servlet-api.jar)

 

 

 


5. 이클립스에 적용하기(Apply to Eclipse)

 

이클립스에 적용하는 방법이다. {프로젝트명} 폴더를 클릭 후 오른쪽 버튼을 클릭한다.

그리고 "Properties"를 클릭한다.

(This is how you apply it to Eclipse.

Right click on the {Project Name} folder.Then click "Properties".)

 

 

 

 

 


6. 소스코드 구현하기(Implement the source code)

 

소스코드를 작성해주면 된다.(Write the source code.)

 

 


7. 디버그 및 태스트하기

 

Eclipse Java 2019-09부터는 Server에 Apache Tomcat이 사라졌다.

(Starting with Eclipse Java 2019-09, Apache Tomcat disappeared from Server.)

 

 


7-1. WAS에 WAR 배포하기

 

WAS(이하 "Web Application Server")에 방금 작성한 자바 프로젝트를 배포하는 방법이다.

(How to deploy a Java project you just created to WAS ("Web Application Server").)

 

 

File->Export를 클릭한다.(Click File-> Export.)

 

 

 

 

배포 위치는 c:\apache-tomcat-***\webapps에 넣어주면 된다.

(The distribution location is in "c:\apache-tomcat-***\webapps.")

 

 


8. 웹 사이트 접속해보기(Go to the website)

 

웹 사이트에 접속하면 확인해볼 수 있다.(You can check it by visiting the website.)

 

반응형
728x90
300x250

[JSP] 3. Hello World - 실제 서버에 올리기(index.jsp) // Hello World-Post to Real Server (index.jsp)


실제 서버에 처음으로 index.jsp를 작성하여 올려보겠다.

리눅스 배포판은 우분투 18.04에서 진행하였다.

(Hello World-Post to Real Server (index.jsp)
Linux distributions was performed in Ubuntu 18.04.)



1. index.jsp 작성하기(Write index.jsp)


아래의 명령어처럼 입력한다.(Enter the following command.)



그림 1) index.jsp 생성하기
Fig 1) Create index.jsp


$ sudo nano /var/lib/tomcat9/webapps/ROOT/index.jsp


아래처럼 내용을 입력한다.



그림 2) index.jsp 내용 작성하기
Fig 2) Write the contents of index.jsp



2. 홈페이지 시연하기(Demonstrate homepage)


시연은 웹 사이트에 접속하면 간단하게 할 수 있다. (eg: 127.0.0.1:8080/index.jsp)
Demonstrations can be made simple by accessing the website (eg: 127.0.0.1:8080/index.jsp).


그림 3) 웹 페이지 시연
Fig 3) Web Pages demonstration

반응형
728x90
300x250

[JSP] 2. 우분투 18.04에서의 아파치, 아파치 톰캣 - 서버 경로, JSP란 무엇인가?

([JSP] 2. Apache, Apache Tomcat on Ubuntu 18.04-What is the server path, JSP?)


여는 글로 웹 디렉토리 경로에 대해서 정리해보고, JSP란 무엇인지 특징을 가지고 간단하게 소개한다.
(The opening article summarizes the Web directory paths and briefly introduces what JSP is.)

1. 아파치 2.4, 아파치 톰캣 9 디렉토리(Apache 2.4, Apache Tomcat 9 directory)


 Apache 2.4

 Apache Tomcat 9

 웹ROOT 디렉토리 : /var/www/html/
 CONF 디렉토리 : /etc/apache2
 LOG 디렉토리 : /var/log/apache2

 HOME 디렉토리 : /usr/share/tomcat9/

 CONF 디렉토리 : /etc/tomcat9/

 LOG 디렉토리 : /var/log/tomcat9/

 웹ROOT 디렉토리 : /var/lib/tomcat9/


2. "JSP란 무엇인가"에 대해서 잠깐 소개한다.
(Let's take a quick look at "What is JSP?")
'웹 어플리케이션'은 웹을 기반으로 실행되는 어플리케이션을 의미한다.
흔히 인터넷 익스플로러, 파이어폭스, 크로미니엄(크롬) 등의 웹 브라우저를 사용해서 사이트에 접속하며, 사이트에 접속한 결과를 웹 브라우저를 통해서 보게 된다.
('Web application' means an application that runs on the web.
You often access the site using a web browser, such as Internet Explorer, Firefox, or Chromium (Chrome), and see the results of accessing the site through a web browser.)

JSP는 JavaServer Pages의 약자로 스크립트 언어이다.
* 자바 언어를 기반으로 하는 스크립트 언어로서 자바가 제공하는 기능을 그대로 사용할 수 있다.
* HTTP와 같은 프로토콜에 따라 클라이언트의 요청을 처리하고 응답한다.
* HTML, XML 등 클라이언트가 요청한 문서를 생성하는데 주로 사용된다.
* 서블릿/EJB 등의 엔터프라이즈 기술들과 잘 융합된다.
* 표현언어, 표현식, 스크립트릿 등 다양한 스크립트 요소와 액션 태그 등을 제공함으로써 보다 쉽게 웹 어플리케이션을
  프로그래밍 할 수 있도록 도와준다.

JSP stands for JavaServer Pages. It is a scripting language.
(* A scripting language based on the Java language that allows you to use the features provided by Java.
* Processes and responds to client requests according to protocols such as HTTP.
* It is mainly used to generate documents requested by clients such as HTML and XML.
It integrates well with enterprise technologies such as Servlets / EJBs.
* Provides various script elements and action tags such as expression language, expression, scriptlet, etc. to help you program web application more easily.)


반응형
728x90
300x250
[Java]  Swing - JOptionPane - showInputDialog

 

자바에서 스윙을 소개하고자 합니다.

JOptionPane의 showInputDialog()에 대해서 요약을 해봤습니다.

 


1. 요약

 

// 메뉴 생성
 JMenuItem testItem1 = new JMenuItem("단순 메시지 입력 상자");
 JMenuItem testItem2 = new JMenuItem("콤보 메시지 입력 상자" );
 
 
 testItem1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {      
           JOptionPane.showInputDialog (null, "메시지 박스") ;
      }
 });
 
 testItem2.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
          String[] selections = { "첫번째값", "두번째값", "세번째값" };
         
          // 데이터 입력 받기
          Object test = JOptionPane.showInputDialog( null, "selectionValues \n이용예제", "제목",
                JOptionPane.QUESTION_MESSAGE, null, selections, "두번째값" );
         
          // 출력 결과 찍어보기
          JOptionPane.showMessageDialog( null, test );      
      }
 });

 


2. 참고자료(Reference)

 

1. javase Doc, http://docs.oracle.com/javase/8/docs/api/javax/swing/JOptionPane.html, Accessed by 2015-06-01

 

반응형

+ Recent posts