Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

게으른 완벽주의자의 개발자 도전기

[Spring Security] 기초 2( 회원가입, 로그인, 로그아웃) 본문

Spring Boot

[Spring Security] 기초 2( 회원가입, 로그인, 로그아웃)

머리방울 2022. 9. 22. 20:35

가입하기

controller

	가입페이지 이동
	 @GetMapping("join")
	 public String joinPage() {
		 return "join";
	 }

join.html

<form action="/join" method="post">
	아이디 : <input type="text" name="memberId"> <br>
	비밀번호 : <input type="password" name="memberPw"> <br>
	이름 : <input type="text" name="memberName"> <br>
	권한 : 
	 <input type="checkbox" value="MANAGER" name="role"> 매니저
	 <input type="checkbox" value="ADMIN" name="role"> 관리자
	<br>
	<input type="submit" value="가입하기">

</form>

mapper

회원가입 
<insert id="join">
INSERT INTO SECURITY_MEMBER (
	MEMBER_ID
	, MEMBER_PW
	, MEMBER_NAME
	, ROLE
	)VALUES(
	#{memberId}
	, #{memberPw}
	, #{memberName}
	, #{role}
	)

</insert>

serviceImpl

	@Override
	public void join(MemberVO memberVO) {
		sqlsession.insert("memberMapper.join", memberVO);
	}

memberVO

@Getter
@Setter
@ToString
public class MemberVO {

	private String memberId;
	private String memberPw;
	private String memberName;
	private String role;
   
	public void setRoleAndPw(PasswordEncoder passwordEncoder) {
		
		★memberPw 가져와서 암호화 하겠다.
		 String pw = passwordEncoder.encode(getMemberPw());
		 setMemberPw(pw);
		 
		 권한에 일반회원(member)을 default로 넣겠다.
		 if(getRole() == null) {
			 setRole("MEMBER");
		 }
         
		 else {
			setRole("MEMBER," + getRole());
		 }
	}
}

controller

	 가입하기
	 @PostMapping("/join")
	 public String join(MemberVO memberVO) {
		 
		 memberVO.setRoleAndPw(passwordEncoder);
		 
		 memberService.join(memberVO);
		 
		 return "login";
	 }

로그인 페이지로 이동

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

로그인 실패했을 때 메세지

 @GetMapping("/loginFail") 
	 public String loginFail(Model model) {
		 model.addAttribute("failMsg", "아이디 혹은 비밀번호가 틀렸습니다");
		 return "login"; 
		 
	 }

Security Config.java

미인증 시 내가 만든 로그인 페이지로 이동하도록 설정
 security.antMatchers("/login").permitAll()
         .formLogin().loginPage("/login");
		 .defaultSuccessUrl("/index") 로그인 성공하면 index 페이지로
		 .failureUrl("/loginFail"); 실패하면 실패 메세지 표시

mapper

<mapper namespace="memberMapper">

<resultMap type="kh.study.security.vo.MemberVO" id="member">
<id column="MEMBER_ID" 			property="memberId"/>
<result column="MEMBER_PW" 		property="memberPw"/>
<result column="MEMBER_NAME" 		property="memberName"/>
<result column="ROLE" 			property="role"/>

</resultMap>


<select id="login" resultMap="member">
	SELECT MEMBER_ID
		, ROLE
		, MEMBER_PW	(아이디만 맞으면 다 로그인 될 수 있기 때문에 비밀번호 가져와서 비교)
	FROM SECURITY_MEMBER
	WHERE MEMBER_ID = #{memberId}
	★<!-- AND MEMBER_PW = #{memberPw} -->
	★security에서는 비밀번호 조회 허용하지 않음
    내부적으로 비밀번호 조회 로직 있음(암호화 되어 있음)

</select>

★security는 기본적으로 아이디, 비밀번호, 권한 3가지를 필수적으로 가져와야 함

비밀번호는 로그인 시도할 때 일치하는지만 판단하고 그 이후로는 알아서 폐기해버린다 

그래서 실질적으로 아이디랑 권한만 가지고 있게 된다

 

serviceimpl

       @Override
	public MemberVO login(String memberId) {
		return sqlsession.selectOne("memberMapper.login", memberId);
	}

 

로그인 실행

로그인 실행 시 controller에 메소드 만들지 않아도 
/login이라는 요청을 post 방식으로 보내면 스프링 내부적으로 로그인 처리를 해준다.

<form action="/login" method="post">
ID <input type="text" name="username"><br>
PW <input type="password" name="password"><br>

<div th:text="${failMsg}"></div> 로그인 실패시 메세지

<input type="submit" value="로그인">

★ Security에서는 id에 name은 무조건 username 
★ password의 name을 무조건 password로 넣는다
</form>

 

로그인 실행 메소드 serviceImpl

  security에서 로그인 실행 userDetailsService라는 인터페이스 제공한다.

  (이를 구현할 클래스 하나 생성해야 함 -> UserDetailsServiceImpl을 우리가 만든다 )

UserDetailsService : 로그인시 실행되는 메소드를 가지고 있는 인터페이스

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService{
   @Resource(name="memberService")
   private MemberService memberService;

   이 메소드가 로그인 시 자동으로 실행된다.
   @Override
	public UserDetails loadUserByUsername(String username){
	 
		MemberVO loginInfo = memberService.login(username);
		입력받은 id(input 태그에 name="username"인 것) 매개변수로	받음
	
      	 if(loginInfo == null) {
		 	System.out.println(username + "이라는 회원은 존재하지 않습니다.");
			강제로 예외를 발생시킴
			throw new UsernameNotFoundException("오류");
	}
   		 UserDetails userDetails = User
						.withUsername(loginInfo.getMemberId())
						.password(loginInfo.getMemberPw())	
						.roles(loginInfo.getRole().split(",")) 
						split를 이용하여 "MEMBER, MANAGER, ADMIN" -> "MEMBER", "MANAGER", "ADMIN" 
						.build();
    
   		 return userDetails;
	}
}

User (org.springframework.security)도 제공한다.

 

.roles(loginInfo.getRole().split(",")) 
split를 이용하여 "MEMBER, MANAGER, ADMIN" -> "MEMBER", "MANAGER", "ADMIN" 

 

우리가 회원가입할 때 권한을 선택하도록 체크박스로 해두었다

이때 두가지 모두를 선택하면 

String role =  "MEMBER, MANAGER, ADMIN" 문자열 자체로 저장되어 버린다.

우리는 각각 권한이 있는 것으로 넣고 싶기 때문에

.split(",")을 이용하여 "MEMBER", "MANAGER", "ADMIN" 각 권한을 구별하고자 함수를 사용함.

 

customizing한 로그인 페이지로 이동 

로그인 실패 화면 예시

DB

1번의 경우 암호화 하지 않았을 때

2, 3번의 경우 암호화 했을 때

 

로그인한 유저의 정보 추출

@GetMapping("/info")
	 public String getInfo(Authentication authentication) {
		 
		 로그인한 유저의 정보를 가져 옴
		 User user = (User)authentication.getPrincipal();
		 
		 System.out.println("id=" + user.getUsername());
		 System.out.println("pw=" + user.getPassword());
		 
		 권한정보
		 List<GrantedAuthority>authoList = new ArrayList<>(user.getAuthorities());
		 
		 for(GrantedAuthority list : authoList) {
			 
			 System.out.println(list.getAuthority());
		 }
		 		 		 
		 return"index";
	 }

비밀번호는 로그인 시 확인 후 폐기해버리기 때문에 null값이 뜨는걸 확인할 수 있다.

 

로그아웃

<form action="/logout" method="post">	

Security를 활용하여 logout을 진행하려면 post방식으로 /logout을 보내주면
controller에 /logout 메소드를 만들지 않아도 실행 된다.

<input type="submit" value="로그아웃">
</form>