[Spring Security] 회원가입, 자동 로그인
// 회원가입
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>