서버/Spring

[Spring Security] 회원가입, 자동 로그인

Hana_h 2023. 6. 22. 00:09

// 회원가입


1. "src/main/resources" > com > test > mapper > MemberMapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.test.mapper.MemberMapper">

</mapper>


2. "com.test.domain" > MemberDTO.java
                                  > AuthDTO.java

package com.test.domain;

import java.util.List;

import lombok.Data;

@Data
public class MemberDTO {
	
	private String userid;
	private String userpw;
	private String username;
	private String regdate;
	private String updatedate;
	private String enabled;
	
	private List<AuthDTO> authlist;

}

 

package com.test.domain;

import lombok.Data;

@Data
public class AuthDTO {
	
	private String userid;
	private String auth;

}


3. "com.test.controller" > MemberController.java > register 메소드 구현

package com.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.test.domain.AuthDTO;
import com.test.domain.MemberDTO;
import com.test.mapper.MemberMapper;

@Controller
public class MemberController {
	
	@Autowired
	private MemberMapper mapper;
	
	@Autowired
	private PasswordEncoder encoder;
	
	@GetMapping("/register.do")
	public String register() {
		
		return "register";
	}

}


4. views > inc > header > link 추가

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

<!--  
	
	스프링 시큐리티 > JSP 페이지 + 권한 적용 > 스프링 시큐리티 표현식
		- hasRole()
		- isAnonymous()
		- isAuthenticated()
-->

<header>
	<h1>Spring Security</h1>
	<ul>
		<li><a href="/security/index.do">Index</a></li>
		
		<sec:authorize access="hasRole('ROLE_MEMBER')">
		<li><a href="/security/member.do">Member</a></li>	
		</sec:authorize>	
		
		<sec:authorize access="hasRole('ROLE_ADMIN')">
		<li><a href="/security/admin.do">Admin</a></li>
		</sec:authorize>
		
		<li class="divider"></li>
		
		<!-- sec:authorize > if문 역할  -->
		<sec:authorize access="isAnonymous()">
		<li><a href="/security/customlogin.do">Login</a></li>
		<li class="divider"></li>
		<li><a href="/security/register.do">Register</a></li>	
		</sec:authorize>
		
		<sec:authorize access="isAuthenticated()">
		<li><a href="/security/customlogout.do">Logout</a></li>
		<li class="divider"></li>
		</sec:authorize>	
	</ul>
</header>


5. root-context.xml > mybatis-spring:scan 추가

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<property name="driverClassName"
			value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="jdbcUrl"
			value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"></property>
		<property name="username" value="hr"></property>
		<property name="password" value="java1234"></property>
	</bean>

	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
		destroy-method="close">
		<constructor-arg ref="hikariConfig"></constructor-arg>
	</bean>

	<bean id="sessionfactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<!-- <property name="mapperLocations"
			value="classpath*:mapper/*.xml"></property> -->
	</bean>

	<!-- <bean class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sessionfactory"></constructor-arg>
	</bean> -->
	
	<mybatis-spring:scan base-package="com.test.mapper"/>

</beans>


6. views > register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>register.jsp</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	
	<h2>Register Page</h2>
	
	<form method="POST" action="/security/registerok.do">
	<table class="vertical">
		<tr>
			<th>아이디</th>
			<td><input type="text" name="userid" required></td>
		</tr>
		<tr>
			<th>암호</th>
			<td><input type="password" name="userpw" required></td>
		</tr>
		<tr>
			<th>이름</th>
			<td><input type="text" name="username" required></td>
		</tr>
		<tr>
			<th>권한</th>
			<td>
				<select name="auth">
					<option value="1">일반회원</option>
					<option value="2">관리자</option>
				</select>
			</td>
		</tr>
	</table>
	<div>
		<input type="submit" value="등록하기">
	</div>
	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
	</form>
	

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

</script>
</body>
</html>


7. "com.test.controller" > MemberController.java > registerok 메소드 구현

package com.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.test.domain.AuthDTO;
import com.test.domain.MemberDTO;
import com.test.mapper.MemberMapper;

@Controller
public class MemberController {
	
	@Autowired
	private MemberMapper mapper;
	
	@Autowired
	private PasswordEncoder encoder;
	
	@GetMapping("/register.do")
	public String register() {
		
		return "register";
	}
	
	@PostMapping("/registerok.do")
	public String registerok(Model model, MemberDTO dto, int auth) {
		
		//암호 인코딩
		dto.setUserpw(encoder.encode(dto.getUserpw()));
		
		int result = mapper.register(dto);
		
		model.addAttribute("result", result);
		
		return "registerok";
	}

}


8. "com.test.mapper" > MemberMapper.java(I)

package com.test.mapper;

import com.test.domain.AuthDTO;
import com.test.domain.MemberDTO;

public interface MemberMapper {

	int register(MemberDTO dto);

}


9. "src/main/resources" > com > test > mapper > MemberMapper.xml > insert 쿼리 짜기

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.test.mapper.MemberMapper">

	<insert id = "register">	
		insert into tbl_member(userid, userpw, username) 
			values (#{userid}, #{userpw}, #{username})			
	</insert>
    
</mapper>


10. "com.test.controller" > MemberController.java > registerok 권한 구현

package com.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.test.domain.AuthDTO;
import com.test.domain.MemberDTO;
import com.test.mapper.MemberMapper;

@Controller
public class MemberController {
	
	@Autowired
	private MemberMapper mapper;
	
	@Autowired
	private PasswordEncoder encoder;
	
	@GetMapping("/register.do")
	public String register() {
		
		return "register";
	}
	
	@PostMapping("/registerok.do")
	public String registerok(Model model, MemberDTO dto, int auth) {
		
		//암호 인코딩
		dto.setUserpw(encoder.encode(dto.getUserpw()));
		
		int result = mapper.register(dto);
		
		if(auth >= 1) {
			AuthDTO adto = new AuthDTO();
			adto.setUserid(dto.getUserid());
			adto.setAuth("ROLE_MEMBER");
			mapper.registerAuth(adto);
		}
		
		if(auth >= 2) {
			AuthDTO adto = new AuthDTO();
			adto.setUserid(dto.getUserid());
			adto.setAuth("ROLE_ADMIN");
			mapper.registerAuth(adto);
		}
		
		model.addAttribute("result", result);
		
		return "registerok";
	}

}


11. "com.test.mapper" > MemberMapper.java(I)

package com.test.mapper;

import com.test.domain.AuthDTO;
import com.test.domain.MemberDTO;

public interface MemberMapper {

	int register(MemberDTO dto);

	void registerAuth(AuthDTO adto);

}


12. "src/main/resources" > com > test > mapper > MemberMapper.xml > 권한 insert 쿼리 짜기

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.test.mapper.MemberMapper">

	<insert id = "register">	
		insert into tbl_member(userid, userpw, username) 
			values (#{userid}, #{userpw}, #{username})			
	</insert>

	<insert id = "registerAuth">		
		insert into tbl_member_auth(userid, auth) 
			values (#{userid}, #{auth}) 4
	</insert>

</mapper>


13. views > registerok.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>registerok.jsp</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	
	<h2>RegisterOk Page</h2>
	
	<div>${result}</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

</script>
</body>
</html>

// 자동 로그인(Remember-me)
- 스프링 시큐리티에 기본으로 탑재
- 쿠키 + DB
- 정해진 테이블 구현



1. script.sql > 테이블 생성

create table persistent_logins (
   username varchar(64) not null,
   series varchar(64) primary key,
   token varchar(64) not null,
   last_used timestamp not null
);


2. security-context.xml > security:remember-me 추가

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 403 처리 담당자 -->
	<bean id="customAccessDenied" class="com.test.auth.CustomAccessDeniedHandler"></bean>

	<!-- 로그인 성공 처리 담당자 -->
	<bean id="customLoginSuccess" class="com.test.auth.CustomLoginSuccessHandler"></bean>
	
	<!-- 암호화 객체 -->
	<!-- <bean id="customPasswordEncoder" class="com.test.auth.CustomNoOpPasswordEncoder"></bean> -->
		
	<!-- 암호화 객체(사용자 정의) -->
	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>	
	
	<!-- 유저 정보 객체(기존 Spring에서 제공하는 서비스 커스텀) -->
    <bean id="customUserDetailsService" class="com.test.auth.CustomUserDetailsService"></bean>
   
	<security:http>
		<!-- 특정 URI + 액세스 설정 -->
		<!--  
			pattern: URI 패턴
			access: 권한 > 표현식(권장★★★) or 권한명
			
			인증 > Role(자격 - 관리자,매니저,팀장,담당자)
				> Authority(권한 - 글쓰기,열람,승인) 
		-->
		<security:intercept-url pattern="/index.do" access="permitAll"/> 
		<security:intercept-url pattern="/member.do" access="hasRole('ROLE_MEMBER')"/> 
		<security:intercept-url pattern="/admin.do" access="hasRole('ROLE_ADMIN')"/> 
		<security:form-login login-page="/customlogin.do"
			authentication-success-handler-ref="customLoginSuccess"/>
		
		<!-- ref에 bean id 넣기 -->
		<security:access-denied-handler ref="customAccessDenied"/> 
		
		<!-- 로그아웃 -->
		<security:logout logout-url="/customlogout.do" invalidate-session="true" logout-success-url="/index.do"/>		
		
		<!-- 자동 로그인(8640000 > 3개월) -->
		<security:remember-me data-source-ref="dataSource" token-validity-seconds="8640000"/>
		
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider user-service-ref="customUserDetailsService">
			<security:password-encoder ref="bcryptPasswordEncoder"/>
		</security:authentication-provider>
	</security:authentication-manager>

</beans>


3. customlogin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<%@ include file="/WEB-INF/views/inc/header.jsp" %>
	
	<h2>Custom Login Page</h2>

	<div class="message">${error}</div>
	<div class="message">${logout}</div>
	
	
	<form method="POST" action="/security/login">
	<table>
		<tr>
			<th>아이디</th>
			<td><input type="text" name="username" required></td>
		</tr>
		<tr>
			<th>암호</th>
			<td><input type="password" name="password" required></td>
		</tr>
		<tr>
			<td colspan="2">
				<input type="checkbox" name="remember-me">
				자동 로그인
			</td>
		</tr>
	</table>
	<div>
		<button class="in">로그인</button>
	</div>
	
	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
	
	</form>	

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

</script>
</body>
</html>