[portfolio] 스프링 시큐리티로 로그인/로그아웃 구현

박이레·2023년 10월 25일
0

portfolio

목록 보기
16/20

 스프링 시큐리티 2.7.5


의존성 추가

view는 JSP를 사용할 것이기에 미리 추가해둡니다.

/* Spring Security */
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

/* Spring security taglib for JSP */
implementation 'org.springframework.security:spring-security-taglibs'

SecurityConfig 작성

Spring Security의 설정을 진행합니다.

package com.iraefolio.config;

import com.iraefolio.service.security.CustomUserDetailsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Log4j2
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig {

    /* 자원 접근 조정 및 csrf 토근 관리 */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        /* 로그인 페이지 지정 */
        http.formLogin().loginPage("/login").defaultSuccessUrl("/");

        http.logout().logoutSuccessUrl("/");

        /* CSRF 비활성화 */
        http.csrf().disable();

        return http.build();
    }

    /* 정적 자원 허용 */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations()));
    }

    /* 비밀번호 인코더 */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

UserDetailsService 작성

package com.iraefolio.service.security;

import com.iraefolio.domain.Member;
import com.iraefolio.mapper.AccountMapper;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Log4j2
@RequiredArgsConstructor
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final AccountMapper accountMapper;

    /* 회원 가입 */
    public boolean create(Member member){
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        member.setUsername(member.getUsername());
        member.setPassword(passwordEncoder.encode(member.getPassword()));
        member.setName(member.getName());
        boolean result = accountMapper.create(member);

        return result;
    }

    /* 로그인 */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        Optional<Member> result = accountMapper.findByUserName(username);

        if (result.isEmpty()) throw new UsernameNotFoundException("not found");

        Member member = result.get();

        return member;

    }
}

UserDetails Entity 작성

package com.iraefolio.domain;

import lombok.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

@Setter
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "roleSet")
public class Member implements UserDetails {

    private String username;
    private String password;
    private String name;
    private String role;
    private boolean del;
    private boolean social;

    public void changePassword(String password) {
        this.password = password;
    }

    public void changeName(String name) {
        this.name = name;
    }

    public void changeDel(boolean del) {
        this.del = del;
    }

    public void changeSocial(boolean social) {
        this.social = social;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singletonList(new SimpleGrantedAuthority(this.role));
    }

    @Override
    public boolean isAccountNonExpired() {
        return !del;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !del;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return !del;
    }

    @Override
    public boolean isEnabled() {
        return !del && !social;
    }
}

JSP 작성

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>

<!-- 사용자 정보 -->
<sec:authorize access="isAuthenticated()">
  <sec:authentication property="principal" var="user"/>
</sec:authorize>

<!-- JS 전달용 -->
<script type="text/javascript">
  let currentUser = '${user}';
</script>
profile
혜화동 사는 Architect

0개의 댓글