Spring 강의 day 5

주세환·2023년 5월 3일
0

Spring

목록 보기
5/18

Customer

회원가입

CustomerController

@Controller
@RequestMapping(value = "/customer")
@Slf4j
public class CustomerController {
    final String format = "CustomerController = {}";
    
    @GetMapping(value ="/home.do")
    public String homeGET() {
        return "/customer/home";
    }

    @GetMapping(value = "/join.do")
    public String joinGET() {
        return "/customer/join";
    }

    @PostMapping(value = "/join.do")
    public String joinPOST( @ModelAttribute Member member ){
        log.info(format, member.toString());
        return "/redirect:joinok.do"; // 주소창에 127.0.0.1:9090/ROOT/customer/joinok.do 입력 후 엔터키를 자동화
    }
}

회원가입에 사용할 Controller를 작성하고


join.html

<body>
    <h3>고객용 회원가입화면</h3>
    <form th:action="@{/customer/join.do}" method="post">
    아이디 : <input type="text" name="id" autofocus /><br />
    암호 : <input type="text" name="password" value="a" /><br />
    이름 : <input type="text" name="name" value="가나다" /><br />
    나이 : <input type="number" name="age" value="11" /><br />
    권한 변경(X)<input type="text" name="role" value="CUSTOMER" readonly/><br />
    <input type="submit" value="회원가입" />
    </form>
</body>
</html>

join.html을 작성하여 화면을 만든다.


CustomerController 암호화

@Controller
@RequestMapping(value = "/customer")
@Slf4j
@RequiredArgsConstructor
public class CustomerController {
    final String format = "CustomerController = {}";
    final MemberMapper memberMapper;

    @GetMapping(value ="/home.do")
    public String homeGET() {
        return "/customer/home.do";
    }

    @GetMapping(value = "/join.do")
    public String joinGET() {
        return "/customer/join";
    }

    @PostMapping(value = "/join.do")
    public String joinPOST( @ModelAttribute Member member ){
        log.info(format, member.toString()); // 화면에 정확하게 표시되고 사용자가 입력한 항목을 member객에체 저장했음 확인

        BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder(); // salt값을 자동으로 부여함. (암호화)
        member.setPassword(bcpe.encode(member.getPassword())); // 기존 암호를 암호화시켜서 다시 저장함.
        int ret = memberMapper.insertMemberOne(member);
        if( ret == 1 ){
            return "redirect:joinok.do"; // 주소창에 127.0.0.1:9090/ROOT/customer/joinok.do 입력 후 엔터키를 자동화
        }
        return "redirect:join.do"; // 실패시 회원가입화면으로
    }
    @GetMapping(value ="/joinok.do")
    public String joinokGET() {
        return "/customer/joinok.do";
    }
}

CustomerController를 위처럼 수정하여 암호화 기능과 joinokGET을 추가한다.


joinok.html

<body>
    <p>회원가입되었습니다.</p>
    <a th:href="@{/customer/home.do}">홈으로</a>
</body>

joinok도 수정한다.


이렇게 회원가입을 하면

성공창이 뜨고

이렇게 DB에 등록된다.

로그인

@GetMapping(value ="/login.do")
public String loginGET() {
    return "/customer/login";
}

CustomerController.java에 loginGET을 추가하고


<body>
    <h3>고객용 로그인화면</h3>
    <form th:action="@{/customer/loginaction.do}" method="post">
        아이디 : <input type="text" name="id" autofocus /><br />
        암호 : <input type="text" name="password" value="a" /><br />
        <input type="submit" value="로그인" />
    </form>
</body>
</html>

join.html을 생성한다.


SecurityConfig

@Configuration   // 환경설정파일. 서버가 구동되기전에 호출됨.
@EnableWebSecurity // 시큐리티를 사용
@Slf4j
@RequiredArgsConstructor
public class SecurityConfig {
    
    final UserDetailsService userDetailsService; // 구현한 서비스 SecurityServiceImpl.java
       
    @Bean   // 객체를 생성함. (자동으로 호출됨.)
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        log.info("SecurityConfig => {}", "start filter chain");

        // 권한 설정
        http.authorizeRequests().anyRequest().permitAll();

        // 로그인 처리
        http.formLogin()
            .loginPage("/login.do")  //
            .loginProcessingUrl("/loginaction.do") //action은 
            .usernameParameter("id") 
            .passwordParameter("password")
            .defaultSuccessUrl("/customer/home.do") // 로그인 성공시 이동할 페이지
            .permitAll();

        //서비스 등록
        http.userDetailsService(userDetailsService); 

        return http.build();
    }

    // 정적 자원에 스프링 시큐리티 필터 규칙을 적용하지 않도록 설정, resources/static은 시큐리티 적용받지 않음.
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

    // 회원가입에서 사용했던 암호화 알고리즘 설정, 로그인에서도 같은 것을 사용해야 하니까
    @Bean  // 서버구동시 자동으로 실행됨 => @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

SecurityConfig를 위처럼 수정, 추가한다.

SecurityServiceImpl

@Service
@Slf4j
@RequiredArgsConstructor
public class SecurityServiceImpl implements UserDetailsService{
    final String format = "SecurityServiceImpl => {}";
    final MemberMapper memberMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info(format, username);
        // 아이디를 전달해서 정보를 받아옴 암호까지 받아옴
        Member member = memberMapper.selectMemberOne1(username);
        if( member != null ){ // 가져올 정보가 있었다. 존재하는 아이디가 있음
            // Member DTO를 사용해서 처리하나 시큐리티는 User DTO를 사용함
            return User.builder()
                .username(member.getId())
                .password(member.getPassword())
                .roles(member.getRole()) 
                .build();
        }  
        return User.builder()
            .username("_")
            .password("_")
            .roles("_")
            .build();
    }
}

service 폴더에 SecurityServiceImpl.java를 생성하여 위처럼 추가한다.

mapper

public Member selectMemberOne1(String userid);
<select id="selectMemberOne1" parameterType="com.example.dto.Member" resultType="com.example.dto.Member">
    SELECT m.* FROM member m WHERE m.id = #{id}
</select>

아이디로 정보를 불러올 수 있도록 MemberMapper, memberMapper.xml에 각각 추가한다.


로그인을 해보자

로그인 정보가 일치하지 않을 때 주소창 뒤에 ?error가 붙는다.

올바른 정보로 로그인을 하면 홈화면으로 이동한다.


권한 설정

// 권한 설정
http.authorizeRequests()
    .antMatchers("/customer/join.do").permitAll()
    .antMatchers("/seller/join.do").permitAll()
    .antMatchers("/admin/join.do").permitAll()

    .antMatchers("/admin", "/admin/*").hasAuthority("ROLE_ADMIN")    // 주소가 9090/ROOT/admin ~~ 모든 것
    .antMatchers("/seller", "/seller/*").hasAnyAuthority("ROLE_ADMIN", "ROLE_SELLER")
    .antMatchers("/customer", "/customer/*").hasAnyAuthority("ROLE_CUSTOMER")
    
    .anyRequest().permitAll();

SecurityConfig의 권한 설정을 위처럼 수정한다.

antMatchers는 주소, hasAnyAuthority는 해당 주소에 들어갈 수 있는 권한이다.

CUSTOMER로 로그인 한 상태에서

/seller/home.do로 진입하니 오류가 뜨는 화면을 볼 수 있다.


403page

// 403페이지 설정(접근권한 불가 시 표시할 화면)
http.exceptionHandling().accessDeniedPage("/403page.do");

SecurityConfig에 위 코드를 추가한다.


// 127.0.0.1:9090/ROOT/main.do
@GetMapping(value ="/403page.do")
public String pageGET(){
    return "403page";
}

HomeController에 위 코드를 추가하고


<body>
    <h3>접근불가</h3>
    <a th:href="@{/customer/home.do}">홈으로</a>
</body>

403page.html을 생성하여 위 코드를 입력한다.

403에러 대신 위 사진처럼 뜬다.


로그아웃

@Bean   // 객체를 생성함. (자동으로 호출됨.)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    log.info("SecurityConfig => {}", "start filter chain");	

...

	// 로그아웃 처리(GET은 안됨 반드시 POST로 호출해야 됨.)
    http.logout()
        .logoutUrl("/logout.do")
        .logoutSuccessUrl("/home.do")
        .invalidateHttpSession(true)
        .clearAuthentication(true)
        .permitAll();

...

}

SecurityConfig에 로그아웃을 추가한다.


홈화면

<body style="padding:10px">
    <h3>홈 화면</h3>
    <hr />

    <div th:if="${user == null}">
        <a th:href="@{/customer/join.do}">고객 회원가입</a>
        <a th:href="@{/login.do}">고객 로그인</a>
    </div>

    <div th:if="${user != null}">
        <p th:text="${user.username}"></p>
        <p th:text="${user.authorities}"></p>
        <a th:href="@{/customer/home.do}">고객 홈(CUSTOMER)</a>
        <form th:action="@{/logout.do}" method="post">
            <input type="submit" value="로그아웃" />
        </form>
    </div>

    <hr />

    <div th:if="${user == null}">
        <a th:href="@{/seller/join.do}">판매자 회원가입</a>
        <a th:href="@{/login.do}">판매자 로그인</a>
    </div>

    <div th:if="${user != null}">
        <p th:text="${user.username}"></p>
        <p th:text="${user.authorities}"></p>
        <a th:href="@{/seller/home.do}">판매자 홈(ADMIN, SELLER)</a>
        <form th:action="@{/logout.do}" method="post">
            <input type="submit" value="로그아웃" />
        </form>
    </div>
    
    <hr />

    <div th:if="${user == null}">
        <a th:href="@{/admin/join.do}">관리자 회원가입</a>
        <a th:href="@{/login.do}">관리자 로그인</a>
    </div>

    <div th:if="${user != null}">
        <p th:text="${user.username}"></p>
        <p th:text="${user.authorities}"></p>
        <a th:href="@{/admin/home.do}">관리자 홈(ADMIN)</a>
        <form th:action="@{/logout.do}" method="post">
            <input type="submit" value="로그아웃" />
        </form>
    </div>
</body>

templates 폴더에 home.html을 만든다.

로그인하기 전

로그인했을 때


판매자 계정으로 고객, 관리자 홈으로 들어갈 때

판매자 홈으로 들어갔을 때


LoginSuccessHandler

@Slf4j
public class LoginSuccessHandler implements AuthenticationSuccessHandler{

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        log.info("LoginSuccessHandler => {}", authentication.toString());

        String role = authentication.getAuthorities().toArray()[0].toString();
        log.info("LoginSuccessHandler => {}", role);

        if(role.equals("ROLE_CUSTOMER")) { // 권한이 고객
            response.sendRedirect(request.getContextPath() + "/customer/home.do");
        }
        else if(role.equals("ROLE_SELLER")){ // 권한이 판매자
            response.sendRedirect(request.getContextPath() + "/seller/home.do");
        }
        else if(role.equals("ROLE_ADMIN")) { // 권한이 관리자
            response.sendRedirect(request.getContextPath() + "/admin/home.do");
        }
        else { // 위의 권한이 아닌 경우
            response.sendRedirect(request.getContextPath() + "/home.do");
        }
    }    
}

example 아래에 handler 폴더를 생성하여 그 안에 LoginSuccessHandler.java를 생성한다.


// 로그인 처리
http.formLogin()
    .loginPage("/login.do")  //
    .loginProcessingUrl("/loginaction.do") //action은 
    .usernameParameter("id") 
    .passwordParameter("password")
    .successHandler(new LoginSuccessHandler())
    // .defaultSuccessUrl("/home.do") // 로그인 성공시 이동할 페이지
    .permitAll();

SecurityConfig를 위처럼 수정한다.

0개의 댓글