반응형
목차
ddl
/* 사용자 */
CREATE TABLE "USER_INFO"
("USER_NO" NUMBER NOT NULL ENABLE,
"USER_ID" VARCHAR2(20) NOT NULL ENABLE,
"USER_PASSWORD" VARCHAR2(250) NOT NULL ENABLE,
"USER_NAME" VARCHAR2(20) NOT NULL ENABLE,
"USER_EMAIL" VARCHAR2(20) NOT NULL ENABLE,
"USE_YN" CHAR(1) DEFAULT 'Y' NOT NULL ENABLE,
"REG_DATE" DATE DEFAULT SYSDATE NOT NULL ENABLE,
"MOD_DATE" DATE DEFAULT SYSDATE,
CONSTRAINT "USER_INFO_PK" PRIMARY KEY ("USER_NO", "USER_ID")
)
;
COMMENT ON COLUMN USER_INFO.USER_NO IS '사용자 순번(시퀀스:USER_NO_SEQ)';
COMMENT ON COLUMN USER_INFO.USER_ID IS '사용자 아이디';
COMMENT ON COLUMN USER_INFO.USER_PASSWORD IS '사용자 비밀번호';
COMMENT ON COLUMN USER_INFO.USER_NAME IS '사용자명';
COMMENT ON COLUMN USER_INFO.USER_EMAIL IS '사용자 이메일';
COMMENT ON COLUMN USER_INFO.USE_YN IS '사용여부';
COMMENT ON COLUMN USER_INFO.REG_DATE IS '등록일';
COMMENT ON COLUMN USER_INFO.MOD_DATE IS '수정일';
--시퀀스
CREATE SEQUENCE USER_NO_SEQ INCREMENT BY 1 MINVALUE 1 MAXVALUE 99999999 CYCLE NOCACHE ORDER ;
의존성 추가
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.11'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.0'
implementation 'org.springframework.boot:spring-boot-starter-validation'
//추가
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
testCompileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
메인메소드에서 시큐리티 설정 제외(안하면 시큐리티 기본 로그인 화면 표시됨)
package study.thboard2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
//exclude 추가
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class ThBoard2Application {
public static void main(String[] args) {
SpringApplication.run(ThBoard2Application.class, args);
}
}
config
package study.thboard2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 인증 및 인가에 대한 설정
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
// 인증 없이 허용할 url
.antMatchers("/", "/listAjax", "/register", "/login", "/css/**", "/assets/**", "/js/**").permitAll()
.anyRequest().authenticated();
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="study.thboard2.mapper.UserMapper">
<!--사용자 정보 저장-->
<insert id="insertUser" parameterType="UserVo">
<selectKey keyProperty="userNo" resultType="INTEGER" order="BEFORE">
SELECT USER_NO_SEQ.NEXTVAL FROM DUAL
</selectKey>
/* 사용자 정보 저장 */
INSERT INTO USER_INFO
(
USER_NO /* 사용자 순번 */
, USER_ID /* 사용자 아이디 */
, USER_PASSWORD /* 사용자 비밀번호 */
, USER_NAME /* 사용자명 */
, USER_EMAIL /* 사용자 이메일 */
, REG_DATE /* 등록일 */
)
VALUES
(
#{userNo}
, #{userId}
, #{userPassword}
, #{userName}
, #{userEmail}
, SYSDATE
)
</insert>
<!--사용자 정보 확인(로그인 시 활용)-->
<select id="selectByUserId" parameterType="map" resultType="UserVo">
/* 사용자 정보 확인(로그인 시 활용) */
SELECT USER_NO AS userNo /* 사용자 순번 */
, USER_ID AS userId /* 사용자 아이디 */
, USER_PASSWORD AS userPassword /* 사용자 비밀번호 */
, USER_NAME AS userName /* 사용자명 */
, USER_EMAIL AS userEmail /* 사용자 이메일 */
, USE_YN AS useYn /* 사용여부 */
, REG_DATE AS regDate /* 등록일 */
, MOD_DATE AS modDate /* 수정일 */
FROM USER_INFO
WHERE USER_ID = #{userId}
</select>
</mapper>
mapper
package study.thboard2.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import study.thboard2.domain.vo.UserVo;
import java.util.List;
@Repository @Mapper
public interface UserMapper {
/* 사용자 정보 저장 */
void insertUser(UserVo userVo);
/* 사용자 정보 확인(로그인 시 활용) */
UserVo selectByUserId(@Param("userId") String userId);
}
service
package study.thboard2.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import study.thboard2.domain.vo.UserVo;
import study.thboard2.mapper.UserMapper;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final UserMapper userMapper;
//아래 추가
private final PasswordEncoder bCryptPasswordEncoder;
/**
* 사용자 정보 저장
* @param userVo
* @throws Exception
*/
@Transactional
public void regUser(UserVo userVo) throws Exception{
//비밀번호 암호화
userVo.hashPassword(bCryptPasswordEncoder);
userMapper.insertUser(userVo);
}
/**
* 아이디/비밀번호 확인
* @param userId
* @param userPassword
* @return
*/
public String login(String userId, String userPassword) throws Exception{
UserVo userInfo = userMapper.selectByUserId(userId);
return (userInfo.checkPassword(userPassword, bCryptPasswordEncoder) == true ? userInfo.getUserId() : "none");
}
}
Vo
package study.thboard2.domain.vo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
@Data
@Slf4j
//사용자 vo
//(vo에 암호화, 비밀번호 비교 메소드를 추가 했음, 서비스에 해도 됨, 이건 스타일)
public class UserVo extends CommonVo {
private Integer no; //사용자 rownum
private Integer userNo; //사용자 순번(시퀀스, pk)
private String userId; //사용자 아이디(pk)
private String userPassword; //사용자 비밀번호
private String userName; //사용자명
private String userEmail; //사용자 이메일
private char useYn; //사용여부
private String modDate;
/**
* 비밀번호 암호화
* @param passwordEncoder
* @return
*/
public UserVo hashPassword(PasswordEncoder passwordEncoder) {
this.userPassword = passwordEncoder.encode(this.userPassword);
return this;
}
/**
* 비밀번호 확인
* @param orgPassword 평문 암호
* @param passwordEncoder
* @return
*/
public boolean checkPassword(String orgPassword, PasswordEncoder passwordEncoder) {
//passwordEncoder.matches가 평문 암호를 해싱 암호화값과 비교 후 true or false return
return passwordEncoder.matches(orgPassword, this.userPassword);
}
}
controller
package study.thboard2.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import study.thboard2.domain.vo.UserVo;
import study.thboard2.service.UserService;
import javax.servlet.http.HttpSession;
@Controller
@Slf4j
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
/**
* 로그인 화면
* @param session
* @return
*/
@GetMapping("/login")
public String loginForm(HttpSession session) {
String id = (String) session.getAttribute("id");
log.info("stored session id =[{}]", id);
return id != null ? "redirect:/" : "pages/login" ;
}
/**
* 회원가입 화면
* @return
*/
@GetMapping("/register")
public String registerForm() {
return "pages/register";
}
/**
* 회원가입 처리
* @param userVo
* @return
*/
@PostMapping("/register")
public String register(@ModelAttribute UserVo userVo) {
try {
userService.regUser(userVo);
} catch (Exception e) {
log.info("Exception => [{}] ", e.getMessage());
}
return "redirect:/login";
}
/**
* 사용자 로그인
* @param userId
* @param userPassword
* @param session
* @return
*/
@PostMapping("/login")
@ResponseBody
public ResponseEntity<?> login(@RequestParam String userId,
@RequestParam String userPassword,
HttpSession session) throws Exception {
String id = userService.login(userId, userPassword);
if(id == "none") return new ResponseEntity<>(0, HttpStatus.BAD_REQUEST);
session.setAttribute("id", id);
return new ResponseEntity<>(1, HttpStatus.OK);
}
}
html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/default_layout}">
<!-- Content -->
<div layout:fragment="content">
<main>
<form id="frm" action="/login" method="post">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="card shadow-lg border-0 rounded-lg mt-5">
<div class="card-header"><h3 class="text-center font-weight-light my-4">Login</h3></div>
<div class="card-body">
<form>
<div class="form-floating mb-3">
<input class="form-control" id="userId" name="userId" type="id" placeholder="name@example.com" />
<label for="inputEmail">ID</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="userPassword" name="userPassword" type="password" placeholder="Password" />
<label for="inputPassword">Password</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />
<label class="form-check-label" for="inputRememberPassword">Remember Password</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="small" href="password.html">Forgot Password?</a>
<!-- <button type="submit" class="btn btn-success">Login</button>-->
<button type="button" id="loginBtn" class="btn btn-success">Login</button>
</div>
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small"><a th:href="@{/register}">회원가입</a></div>
</div>
</div>
</div>
</div>
</div>
</form>
</main>
</div>
<script layout:fragment="script" th:inline="javascript" type="text/javascript">
$(document).ready(function () {
/* 로그인 ajax */
$("#loginBtn").on("click", function () {
$.ajax({
url : '/login',
type : 'post',
dataType : 'json',
data : $("#frm").serialize(),
success: function (result) {
if(result === 1)
window.location = "/";
},
error: function (request, status, error) {
alert("로그인 실패했어. 아이디와 패스워드를 확인하렴");
console.log(error);
window.location = "/login";
}
});
});
});
</script>
</html>
반응형
'IT > development' 카테고리의 다른 글
[thymeleaf] 리터럴 대체 (0) | 2023.06.14 |
---|---|
[JavaScript] 현재날짜와 특정날짜 비교 (0) | 2023.06.13 |
[springBoot/thymeleaf] ajax 페이지네이션 sample(feat. study용) (2) | 2023.06.04 |
[mybatis] mybatis oracle merge into (0) | 2023.06.03 |
[spring] springBoot ajax json과 file 전송 (0) | 2023.06.03 |