Before Start.

spring jpa에 대한 세팅이 끝난 다는 가정하에 진행되는 예제 입니다.

jpa 세팅이 안되신 분은 https://writemylife.tistory.com/87?category=833540 참고 해주세요

 

Step1. 회원 클래스 만들기

user라는 이름으로 제공되는 클래스가 있기 때문에 보통, Account or Member로 회원 클래스를 만듭니다. 저는 Account로 진행 해보겠습니다.

 

src/main/java/패키지명/Account 폴더를 만들고 그 아래 Account.java를 만듭니다.

 

Account.java

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

import javax.persistence.*;
import java.util.Date;
import java.util.List;

@Entity
@Table(name="Account")
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
@Data
public class Account {

    @Id
    private String id;

    @Column(nullable=false)
    private String email;

    @Column(nullable=false)
    private String password;

    @CreationTimestamp
    private Date regdate;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinColumn(name="uid")
    private List<AccountRole> roles;
}

 

Step2. 컨트롤러 만들기

src/main/java/패키지명/Account 아래 AccountController.java를 만듭니다.

 

AccountController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;

@Controller
public class AccountController {

    @Autowired
    AccountService accontService;

    @RequestMapping(value="/create", method = RequestMethod.POST)
    public String create(Account account, HttpServletRequest httpServletRequest) throws Exception{
        accontService.saveAccount(account, httpServletRequest);
        return "redirect:/login";
    }

    @RequestMapping(value="/member/signUp", method = RequestMethod.GET)
    public String signUp(){
        return "/member/signUp";
    }


}

 

Step3. 레포지터리 만들기

src/main/java/패키지명/Account 아래 AccountRepository.interface를 만듭니다.

 

AccountRepository.interface

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

public interface AccountRepository extends CrudRepository<Account, Integer> {


    @Query("SELECT account FROM Account account WHERE account.id = :id")
    Account findById(@Param("id") String id);

}

 

Step4. 권한 등급 클래스 만들기

src/main/java/패키지명/Account 아래 AccountRole.java를 만듭니다.

 

AccountRole.java

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Data
@Entity
@EqualsAndHashCode(of = "rno")
public class AccountRole {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long rno;

    private String roleName;
}

 

Step5. 서비스 클래스 만들기

src/main/java/패키지명/Account 아래 AccountService.java를 만듭니다.

 

AccountService.java

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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

@Service
public class AccountService implements UserDetailsService{

    @Autowired
    private AccountRepository AccountRepository;


    public void saveAccount(Account account, HttpServletRequest httpServletRequest) throws Exception {
        AccountRole role = new AccountRole();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        role.setRoleName("USER");
        account.setRoles(Arrays.asList(role));
        account.setPassword(passwordEncoder.encode(account.getPassword()));
        AccountRepository.save(account);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account account = AccountRepository.findById(username);

        if(account == null)
            throw new UsernameNotFoundException(username);

        SecurityAccount securityAccount = new SecurityAccount(account);
        return securityAccount;
    }
}

 

Step6. 보안 클래스 만들기

src/main/java/패키지명/Account 아래 SecurityAccount.java를 만듭니다.

 

SecurityAccount.java

import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
public class SecurityAccount extends User {
    private static final String ROLE_PREFIX = "ROLE_";
    private static final long serialVersionUID = 1L;

    public SecurityAccount(Account account){
        super(account.getId(), account.getPassword(), makeGrantedAuthority(account.getRoles()));
    }

    private static List<GrantedAuthority> makeGrantedAuthority(List<AccountRole> roles){
        List<GrantedAuthority> list = new ArrayList<>();
        roles.forEach(role -> list.add(new SimpleGrantedAuthority(ROLE_PREFIX + role.getRoleName())));
        return list;
    }

}

 

Step7. 로그인 화면 만들기

공통 css 파일

src/main/resources/static 아래 login.css를 만듭니다.

 

login.css

.login-page {
    width: 100%;
    padding: 8% 0 0;
    margin: auto;
}
a{
    text-decoration-line: none;
}
.form {
    position: relative;
    z-index: 1;
    background: #FFFFFF;
    max-width: 360px;
    margin: 0 auto 100px;
    padding: 45px;
    text-align: center;
    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
.form .label{
    width : 30%;
}
.form .content{
    width : 50%
}
.form input {
    font-family: "Roboto", sans-serif;
    outline: 0;
    background: #f2f2f2;
    width: 100%;
    border: 0;
    margin: 0 0 15px;
    padding: 15px;
    box-sizing: border-box;
    font-size: 14px;
}
.form .submit {
    display : block;
    font-family: "Roboto", sans-serif;
    text-transform: uppercase;
    outline: 0;
    background: #4CAF50;
    width: 100%;
    padding: 15px 0px 15px 0px;
    color: #FFFFFF;
    font-size: 14px;
    cursor: pointer;
    margin-top:5px;
    font-weight: bold;
}
.form .submit:hover,.form .submit:active,.form .submit:focus {
    background: #43A047;
}

.form .message {
    margin: 15px 0 0;
    color: #b3b3b3;
    font-size: 12px;
}
.form .message a {
    color: #4CAF50;
    text-decoration: none;
}
.form .register-form {
    display: none;
}

.check_id{
    width:17px !important;
    height:17px !important;
    margin: 0px 5px 0px !important;
}

.btn{
    font-family: "Roboto", sans-serif;
    text-transform: uppercase;
    outline: 0;
    background: #7D7F82;
    width: 100%;
    border: 0;
    padding: 15px;
    color: #FFFFFF;
    font-size: 14px;
    cursor: pointer;
    margin-top:5px;
    font-weight: bold;
}
body {
    background: #76b852; /* fallback for old browsers */
    background: -webkit-linear-gradient(right, #76b852, #8DC26F);
    background: -moz-linear-gradient(right, #76b852, #8DC26F);
    background: -o-linear-gradient(right, #76b852, #8DC26F);
    background: linear-gradient(to left, #76b852, #8DC26F);
    font-family: "Roboto", sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

 

src/main/resources/templates 아래 login.html를 만듭니다.

 

login.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <link rel="stylesheet" th:href="@{/css/login.css}"/>
</head>
<body>
<div th:if="${param.error}">
    Invalid username and password.
</div>
<div th:if="${param.logout}">
    You have been logged out.
</div>
<div class="login-page">
    <div class="form">
        <form class="login-form" th:action="@{/login}" method="post">
            <input type="text" name="username" placeholder="ID"/>
            <input type="password" name="password" placeholder="Password"/>
            <input class="submit" type="submit" value="로그인">
            <a class="submit" th:href="@{/member/signUp}">회원가입</a>
        </form>
    </div>
</div>
</body>
</html>

Step8. 회원가입 화면 만들기

src/main/resources/templates/member 아래 signUp.html를 만듭니다.

 

signUp.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" th:href="@{/css/login.css}"/>
</head>
<body>
<div class="login-page">
    <div class="form">
        <form class="login-form" th:action="@{/create}" method="post">
            <table>
                <tr>
                    <td class="label">아이디</td>
                    <td class="content"><input type="text" id="id" name="id" placeholder="ID"/></td>
                </tr>
                <tr>
                    <td class="label">이메일</td>
                    <td class="content"><input type="text" id="email" name="email" placeholder="email"/></td>
                </tr>
                <tr>
                    <td class="label">비밀번호</td>
                    <td class="content"><input type="password" id="password" name="password" placeholder="Password"/></td>
                </tr>
            </table>
            <input type="submit" class="submit" value="회원가입">
        </form>
    </div>
</div>
</body>
</html>

 

Step9. WebSecurityConfig 수정

WebSecurityConfig.java의 configure메소드를 수정합니다.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/hello").authenticated()
                .antMatchers("/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

 

Step10. 결과 확인

 

첫 화면 

 

 

hello 이동 누른후 자동으로 로그인 화면으로 이동

 

 

 

회원가입 화면

 

 

 

테이블 확인

 

 

 

 

 

 

 

 

로그인 적용은 다음 글에서 진행하겠습니다~~

출처 및 참고 사이트

https://github.com/kdevkr/spring-demo-security

https://xmfpes.github.io/spring/spring-security/

 

블로그 이미지

발전하는개발자

나의 인생에 필요한 정보들(프로그래밍, 철학, 운동...)

,