출처: https://bumcrush.tistory.com/182 [맑음때때로 여름]

회사에서 소소하게 프로젝트를 시작했는데
기존에 원래 되어있던게 스프링도 아닌 정말 기본 자바 프로젝트;;였다
로그인할땐 특정 uuid를 생성해서 인증key같은 걸로 사용하다가
세션이 종료 되었을때 사라지게 해달라고 요청받음..
방법을 찾다보니 HttpSessionListener를 쓰면 된다고..

 

1. web.xml에 추가하기

 

    <listener>
        <listener-class>egovframework.dev3.common.web.SessionListener</listener-class>
    </listener>

ㅎ.. sessiontimeout은 그냥 테스트하느라 1로 했음

    <session-config>
    		<session-timeout>1</session-timeout>
	</session-config>

 

2. 맨들기

public class SessionListener implements HttpSessionListener {

	@Override
    public void sessionCreated(final HttpSessionEvent event) {

		LocalTime now = LocalTime.now();
	    HttpSession session = event.getSession();
	    System.out.println(session.getId());
	    System.out.println("세션 IN   :   " + now);

    }


	@Override
	public void sessionDestroyed(HttpSessionEvent event) {
		LocalTime now = LocalTime.now();
	    HttpSession session = event.getSession();
	    System.out.println("세션 OUT   :   " + now);
	  }
}

 

 

3. 결과

 

그렇다고 한다.

 

https://stackoverflow.com/questions/47166425/how-to-force-javamailsenderimpl-to-use-tls1-2

 

How to force JavaMailSenderImpl to use TLS1.2?

Have a JDK7 app running on Tomcat and it does have the following env settings: -Dhttps.protocols=TLSv1.1,TLSv1.2 The above setting ensures that we don't use TLS 1.0 when connecting over HTTPS wh...

stackoverflow.com

<dependency>
        <groupId>javax.mail</groupId>
        <artifactId>mail</artifactId>
        <version>1.5.0-b01</version>
</dependency>
Properties prop = new Properties();
prop.setProperty("mail.smtp.auth", "true");
prop.setProperty("mail.smtp.starttls.enable", "true");
prop.setProperty("mail.smtp.ssl.protocols", "TLSv1.2"); // Added this line
prop.setProperty("mail.smtp.ssl.trust", mailUri.getHost());
mailSender.setJavaMailProperties(prop);
        List<Output> rst = new ArrayList<>();

        Gson gson = new GsonBuilder()
                .serializeNulls()
                .create();
        String json = gson.toJson(rst);
        System.out.println(json);

 

 

null값이 전송되지 않고 값이 있는 파라미터만 전송될때..?

사실 null값을 전송하지 말자! 하는

@JsonInclude(value = Include.NON.NULL)

어노테이션을 더 많이 사용하고 있어서

새롭게 찾았는데 안까먹으려고 메모~~~~

const requestIp = require('request-ip');
$ npm install request-ip
let Ip = requestIp.getClientIp(req)

 

- 끗 -

 

----------- 더 간단하게는 

const ip = req.headers['x-forwarded-for'] ||  req.connection.remoteAddress;

그리고 만약 로컬에서 ::1로 사용자 IP가 출력되는 경우가 있다. ::1은 IpV6에서 로컬 호스트를 의미한다. 그래서 express 서버를 생성할 때 IPV4를 사용하도록 설정하면 된다.

 


 ex) localhost는 ::1로 들어오고 다른건 ::fff:000.00.00.0. 이런식..

 

if (params.dlIp.substr(0, 7) == "::ffff:") {
    params.dlIp = params.dlIp.substr(7)
}

 

 

 

 

뭔가 이렇게 바로 다운로드 시키고 싶었음

router.route('/sdk/download').get(async (req, res)=> {
    logger.info('request => [get] /sdk/download');
    
    // 필요한 작업 솰라솰라하고나서   
    
    // 다운로드
    let filepath = '파일경로';
    res.download(filepath)
});

 

그냥 이게 끝이었다 ^_^

 

 

라고 생각했는데 뭔가 구리게 파일이 떨어짐..

파일명 같은게 32ru3ifeiosfhsiodf923이런식으로;

 

const path = require('path');
 let filepath = '파일경로 + 파일명' // ex) /dsdfdsf/dfdfd/dfdf/dd.zip
 res.download(filePath, encodeURIComponent(path.basename(filePath)));

 

이렇게 해주면 파일명도 깰끔~~

'nodejs' 카테고리의 다른 글

node ip 클라이언트 ip 가져오기  (0) 2022.02.14
NODE 에서 파일 저장하기 :: FS 이용  (2) 2022.02.07
[nodejs] 데이터 받기 body-parser  (0) 2021.02.24
[nodejs] nodejs / expressjs 설치  (0) 2021.02.24

스프링에서 파일 저장할때는 그냥 multipart 를 이용해서 슝슝 저장하면 됐는데

노드의 파일 시스템은 fs... 예.. 가져와서 쓴다.

const fs = require('fs');

 

 

파일은 BASE64 STRING으로 받는다~

버퍼를 이용해서 파일 저장..

 

 

저장해따

let file = fs.readFileSync('./message.js', 'utf-8'); //message.js 파일 읽기
let encode = Buffer.from(file).toString('base64'); //파일 base64로 인코딩
let mk = fs.writeFileSync('./encodeFile', encode); // 인코딩된 파일 만들기
let file2 = fs.readFileSync('./encodeFile', 'utf-8'); // 인코딩된 파일 읽기
let decode = Buffer.from(file2, 'base64').toString('utf-8'); //파일 디코딩
let mk2 = fs.writeFileSync('./decodeFile.js', decode); //디코딩된 파일 만들기

 

구글 OAuth

https://console.cloud.google.com/

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

 

1. 새 프로젝트 생성

2. 사용자 동의화면 필요한 정보 입력

3. OAuth 클라리언트 ID 만들기

3-1 웹에서 사용할 거니까 웹 애플리케이션으로 해줌

리다이렉션 URI는 라이브러리를 사용하면 따로 컨트롤러에서 mapping을 해주지 않아도

/login/oauth2/code/google로 고정이댜~ 라이브러리가 알아서 처리를 해준다!

완료하면 클라이언트 ID와 비밀번호가 뜨는데 노출되지 않도록 하자~

 

 

OAuth2.0 라이브러리 사용을 위해 의존성 추가추가!

 

https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-oauth2-clien0

<!--OAUTH-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

application.yml 파일도 수정

 

/oauth2/authorization/google 주소 역시 /login/oauth2/code/google와 같이 이 주소를 써야한다.

하지만 이거만 쓰면 냅다 404가 떠버린다

 

security config 파일에 Oauth로그인페이지 설정을 해주어야한다. 구럼 잘뜸!

.and()
.oauth2Login()
.loginPage("/loginForm"); // oauth로그인페이지

 

로그인은 잘 되지만 권한이 없는 manager 페이지 같은 경우에는 403이뜬다.

이 경우에는 구글 로그인이 완료된 뒤의 후처리가 필요하다.

[ webSecurityConfigurerAdapter를 상속한 sercurityConfig ]

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) 추가

// secured 어노테이션 활성화 & preAuthorize/PostAuthorize 어노테이션 활성화

package com.cos.security1.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;

@Configuration
@EnableWebSecurity // 활성화 - 스프링 시큐리티 필터가 스프링 필터체인에 등록이 된다
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // secured 어노테이션 활성화 & preAuthorize/PostAuthorize 어노테이션 활성화
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable(); // /login으로 갈때 스프링 시큐리티가 낚아채지 않도록

        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll()// 다른주소는 OK
                .and()
                .formLogin()
                .loginPage("/loginForm") // 로그인안햇을때 403페이지가 뜨는게 아니라 login페이지를 뜨게만들어줌
                .loginProcessingUrl("/login") //login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행
                .defaultSuccessUrl("/"); // 로그인이 완료되면 이동할 주소


    }

    @Bean // 해당 메서드의 리턴되는 오브젝트를 ioc로 등록
    public BCryptPasswordEncoder encodePwd(){
        // 비밀번호 암호화를 위해
        return new BCryptPasswordEncoder();
    }
}

[ Contoller ]

postAuthorize는 잘 사용하지 않는다.

실행시점?의 차이. 

@Secured("ROLE_ADMIN")
@GetMapping("/info")
public @ResponseBody String info(){
    return "개인정보";
}

@PreAuthorize("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")
// @PostAuthorize()
@GetMapping("/data")
public @ResponseBody String data(){
    return "데이터정보";
}

로그인폼과 회원가입폼 HTML

더보기

로그인폼과 회원가입폼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>로그인 페이지</title>
</head>
<body>
    <h1>로그인 페이지</h1>
<hr>
 <form action="/login" method="POST">
     <input type="text" name="username" placeholder="UserName"/><br/>
     <input type="password" name="password" placeholder="Password"/><br/>
     <button>로그인</button>
 </form>

<a href = "/joinForm">회원가입을 아직 않으셨나요?</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>회원가입 페이지</title>
</head>
<body>
<h1>회원가입 페이지</h1>
<form action="/join" method="POST">
    <input type="text" name="username" placeholder="UserName"/><br/>
    <input type="password" name="password" placeholder="Password"/><br/>
    <input type="email" name="email" placeholder="Email"/><br/>
    <button>회원가입</button>
</form>

</body>
</html>

 

[ 회원가입 Contoller ]

  @PostMapping("/join")
    public String join(User user){

        System.out.println(user);


        // 2. 패스워드 암호화를 추가해줌 securityConfig에도 BcryptpasswordEncoder를 @Bean으로 등록
        String rawPassword = user.getPassword();
        String encPassword = bCryptPasswordEncoder.encode(rawPassword);
        user.setPassword(encPassword);

        
        // 1. userRepository.save(user); // 회원가입 잘됨. 비밀번호 : 1234 => 시큐리티로 로그인을 할수 없음,
        // :: 패스워드가 암호화가 안되었기 때문
        user.setRole("ROLE_USER");
        userRepository.save(user);


        return "redirect:/loginForm";
    }

[ USER MODEL ]

package com.cos.security1.model;

import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.sql.Timestamp;

@Entity
@Data
public class User {

    @Id // primary Key
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private  int id;
    private String username;
    private String password;
    private String email;
    private String role; // ROLE_USEr, ROLE_ADMIN
    @CreationTimestamp
    private Timestamp createdate;

}

 

[ USER REPORSITORY - JPA ]

package com.cos.security1.repository;

import com.cos.security1.model.User;
import org.springframework.data.jpa.repository.JpaRepository;


// CRUD 함수를 JPArepository가 들고 있음
// @Repository라는 어노테이션이 없어도 Ioc가 됨 (자동 빈등록)
// -> JpaRepository를 상속했기 때문
public interface UserRepository extends JpaRepository<User, Integer> {


    // findBy규칙 -> username 문법
    // seelct * from user where username = #{username}
    public User findByUsername(String username);
}

 

[ config 수정 ]

밑 두줄 추가.

 


public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable(); // /login으로 갈때 스프링 시큐리티가 낚아채지 않도록

        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll()// 다른주소는 OK
                .and()
                .formLogin()
                .loginPage("/loginForm") // 로그인안햇을때 403페이지가 뜨는게 아니라 login페이지를 뜨게만들어줌
                .loginProcessingUrl("/login") //login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행
                .defaultSuccessUrl("/"); // 로그인이 완료되면 이동할 주소


    }

 

 

[ userDetails를 상속하는 클래스 생성 ]

유저정보를 저장.

package com.cos.security1.config.auth;

// 시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킴
// 로그인 진행이 완료되면 seisson을 만들어줌 (Security ContextHolder)
// 세션에 들어갈 수 있는 정보(오브젝트) > authentication 타입의 객체
// authentication 안에 User정보가 있어야 함.
// user 오브젝트 타입 > UserDetails 타입 객체

// Security Session => Authentication => UserDetails(PrincipalDetails)

import com.cos.security1.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;

public class PrincipalDetails implements UserDetails {

    private User user; // 콤포지션

    public PrincipalDetails(User user){
        this.user = user;
    }


    // 해당 user의 권한을 리턴하는 곳
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collect = new ArrayList<>();
        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });
        return collect;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }


    // 계정 만료
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    // 계정 잠김
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    // 계정 유효기간
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    // 계정 정지
    @Override
    public boolean isEnabled() {

        // ex) 휴면계정
        // 현재시간 - 로그인시간 => 1년초과하면 return false

        return true;
    }
}

 

[ userDetailsService를 상속한 클래스 만들기 ]

이름 그대로 유저의 정보를 저장해주는 서비스

// 시큐리티 설정에서 loginProcessingUrl("/login")
// /login 요청이 오면 자동으로 UserDetailsService 타입으로 ioc되어있는 loadUserByUsername가 함수 실행
package com.cos.security1.config.auth;

import com.cos.security1.model.User;
import com.cos.security1.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;


// 시큐리티 설정에서 loginProcessingUrl("/login")
// /login 요청이 오면 자동으로 UserDetailsService 타입으로 ioc되어있는 loadUserByUsername가 함수 실행

@Service
public class PrincaipalDetailsService implements UserDetailsService{


    @Autowired
    private UserRepository userRepository;

    // 시큐리티 session > authentication > userDetails(principalDetails)
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // html에 꼭 "username"으로 해야함 (String username)과 매칭이 안됨
        // 바꾸고 싶다면 security config에 .usernameParameter("username2")
        // 그냥 username을 쓰자 ^^

        User userEntity = userRepository.findByUsername(username);
        if(userEntity != null){
            return new PrincipalDetails(userEntity);
        }

        return null;
    }
}

[ userRepository 에는 유저의 정보를 조회하는 부분 추가 ]

    // findBy규칙 -> username 문법
    // seelct * from user where username = #{username}
    public User findByUsername(String username);

 

spring-boot-starter-security 의존성 주입 

 

 

[ controller ]

package com.cos.security1.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class indexController {

    @GetMapping({"","/"})
    public String index(){
        // mustache 머스태치
        // 머스태치 기본폴더 src/main/resources/
        // viewResolver 설정 : templates (prefix), mustache (suffix)
        return "index"; //  src/main/resources/templates/index.mustache
    }

    @GetMapping("/user")
    public  @ResponseBody String user(){
        return "user";
    }

    @GetMapping("/admin")
    public  @ResponseBody String admin(){
        return "admin";
    }

    @GetMapping("/manager")
    public  @ResponseBody String manager(){
        return "manager";
    }

    // 스프링시큐리티가 해당주소를 낚아채버림
    // SecurityConfig 파일에서 설정으로 낚아채지 않도록 !
    @GetMapping("/login")
    public  @ResponseBody String login(){
        return "login";
    }

    @GetMapping("/join")
    public  @ResponseBody String join(){
        return "join";
    }

    @GetMapping("/joinProc")
    public @ResponseBody String joinProc(){
        return "회원가입 완료됨!";
    }
}

 

[ config ] 

package com.cos.security1.config;


import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity // 활성화 - 스프링 시큐리티 필터가 스프링 필터체인에 등록이 된다.
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable(); // /login으로 갈때 스프링 시큐리티가 낚아채지 않도록

        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll()// 다른주소는 OK
                .and()
                .formLogin()
                .loginPage("/login"); // 권한이 없다면 403페이지가 뜨는게 아니라 login페이지를 뜨게만들어줌

    }
}

 

[ webMvcConfiguer ]

- 머스태치 설정

- view 주소 설정

package com.cos.security1.config;

import org.springframework.boot.web.servlet.view.MustacheViewResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig  implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        MustacheViewResolver resolver = new MustacheViewResolver();
        resolver.setCharset("UTF-8");
        resolver.setContentType("text/html; charset=UTF-8");
        resolver.setPrefix("classpath:/templates/");
        resolver.setSuffix(".html"); // .mustache가 아닌 .html로 바꿔주기 위한 작업

        registry.viewResolver(resolver); // viewResolver 등록
    }
}

+ Recent posts