일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 마이페이지
- 배포
- 밸류즈 홈페이지
- Update
- 로그인
- RCPS
- 이미지 업로드
- 빌드 및 배포
- Ajax
- 로그인 로직
- 캘린더 라이브러리
- 회원가입로직
- 관리자페이지
- MRC
- 달력 라이브러리
- 파생상품평가
- ui탬플릿
- 인증처리
- jsonwebtoken
- stock option
- 공통메서드
- 스프링시큐리티
- register
- react
- 밸류즈
- Token
- mypage
- Typesciprt
- Styled Components
- userManagement
- Today
- Total
I T H
[포트폴리오 프로젝트 5] 로그인 페이지 구현(spring security포함) 본문
로그인 구현에 앞서 폴더 구성을 변경하고자 한다.
아래와 같이 index.jsp를 제외한 앞으로 신규 생성될 jsp 파일들은 WEB-INF/views 폴더 아래에 위치시키도록 한다.

- 앞서 admin.jsp 페이지 위치가 다르게 설정되어 있었으므로 아래와 같이 컨트롤러도 수정하여 준다.

- 로그인 구현을 위한 화면 UI를 작성한다.
[ JSP ]
WEB-INF\views\cLogin.jsp
아이디, 비밀번호 입력 창이 가운데 배치가 되도록 스타일 수정을 해주었다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="kr">
<%@include file="/resources/inc/header.jsp"%>
<style>
.row {
justify-content: center;
display: flex;
padding: 5px;
}
</style>
<body class="home">
<%@include file="/resources/inc/top.jsp"%>
<main id="main">
<div class="container">
<div class="section featured topspace">
<h2 class="section-title">
<span>로그인 페이지</span>
</h2>
<div class="row">
<div class="col-sm-6 col-md-6">
<h3 class="text-center">로그인 정보를 입력하세요.</h3>
</div>
</div>
<div style="" class="col-md-4 col-sm-4"></div> <!-- 빈 div 태그로 여백주기 -->
<form id="loginForm" method="POST" action="/login">
<!-- SecurityConfig에 .loginProcessingUrl("/login")으로 등록되어있기 때문에 action시 AuthProvider를 타게됨!.-->
<div class="col-md-4 col-sm-4"
style="border: 1px solid #c5cfcb; border-radius: 5px; padding: 15px;">
<div class="row">
<div class="col-sm-12 col-md-12">
<label class="form-label" for="txtId">아이디</label> <input
type="text" id="txtId" name="userId" class="form-control">
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12">
<label class="form-label" for="txtPassword">비밀번호</label> <input
type="password" id="txtPassword" name="password"
class="form-control">
</div>
</div>
<p></p>
<div class="row">
<div class="text-center col-sm-12 col-md-12">
<a id="btnLogin" class="btn btn-block w-btn-green button-hover">로그인</a>
</div>
</div>
</div>
</form>
</div>
<!-- / section -->
</div>
<!-- /container -->
</main>
<%@include file="/resources/inc/footer.jsp"%>
<%@include file="/resources/inc/incJs.jsp"%>
<!-- page script -->
<script src="/resources/views/login.js"></script>
</body>
</html>
다음으로 스크립트를 작성한다.
아이디와 비밀번호를 입력 후 로그인 버튼을 클릭하면 서버사이드 컨트롤러를 호출하여 로그인 체크를 하도록 해준다.
[ JS ]
webapp\resources\views\login.js
/*******************************************************************************
* login.js
* @author thevalue
* @since 2023
* @DESC 로그인 화면 스크립트
******************************************************************************/
(function(){
function Login(){
//private variables
//초기화 메서드
function _init(){
//이벤트 처리 함수 호출
bindEvent();
LoginTest();
}
function bindEvent(){
$("#btnLogin").on("click", function(){
var userId = $("#txtId").val();
if(userId == ""){
alert("사용자 아이디를 입력하시기 바랍니다.");
return;
}
var password = $("#txtPassword").val();
if(password == ""){
alert("사용자 비밀번호를 입력하시기 바랍니다.");
return;
}
$("#loginForm").submit();
});
}
function LoginTest(){
var LoginParameter = window.location.href;
//alert(LoginParameter);
if(LoginParameter.indexOf('error') > 0){
alert("입력하신 사용자 정보가 없습니다.");
}
}
function _finalize() {
}
return {
init: _init,
finalize: _finalize
}
}
var login = new Login();
login.init();
})();
- 시큐리티 환경설정 클래스를 작성해준다.
[SecurityInitializer.java]
src\main\java\kr\co\values\security\SecurityInitializer.java
package kr.co.values.security;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/* Spring Security를 사용하려면 AbstractSecurityWebApplicationInitializer를
* 상속받는 클래스를 반드시 작성해야 한다. 이 클래스가 있을 경우
* Spring Security가 제공하는 필터들을 사용할 수 있도록 활성화 해준다.
*/
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer{
}
[ SecurityConfig.java ]
src\main\java\kr\co\values\security\SecurityConfig.java
로그인 페이지 및 로그인 프로세스를 처리할 url 매핑이 configure 메소드에 정의되어 있어야 한다.
package kr.co.values.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/* Spring Security를 이용해 로그인/로그아웃/인증/인가 등을 처리하기 위한 설정 파일이다.
* @EnableWebSecurity가 붙어 있을 경우 Spring Security를 구성하는 기본적인 Bean들을 자동으로 구성해준다.
* WebSecurityConfigurerAdapter를 상속받으면 특정 메소드를 오버라이딩 함으로써 좀 더 손쉽게 설정할 수 있다.
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
/*
* 주의) configure() 메소드는 파라미터 타입이 다른 동일한 메소드명이 존재하므로 확인 필수
* 인증 및 인가가 필요없는 url 을 지정하여 시큐리티 미적용되도록 처리한다.
* ex > /resources /css /img 등등
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/webjars/**");
}
/* configure(HttpSecurity http) 메소드를 오버라이딩 한다는 것은 인증/인가에 대한 설정을 한다는 의미이다.
* 가장 중요한 메소드로 볼 수 있다.
*
* http.csrf().disable()는 csrf() 기능을 끄라는 설정이다.
* csrf는 보안 설정 중 post방식으로 값을 전송할 때 token을 사용해야하는 보안 설정이다.
* csrf은 기본으로 설정되어 있는데 사용시 보안성은 높아지지만
* 개발초기에는 불편함이 있다는 단점이 있어서 기능을 끈 것이다.
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')") 이렇게도 사용 가능하다.
//url주소가 /admin/에 모든것들로 접속하는것들은 "ADMIN"만 가능
//AuthProver에서 이부분 => roles.add(new SimpleGrantedAuthority("ROLE_" + user.getUserAuth()));
.anyRequest().permitAll(); // permitAll() : 이 외 모든 url 접근 허용 / authenticated() : 이 외 모든 url 접근은 로그인 후 사용 가능하도록 설정
//login 설정
http
.formLogin()
.loginPage("/login") //GET 요청 (login form을 보여줌)
.loginProcessingUrl("/login") //POST 요청(login 창에 입력한 데이터를 처리)
.usernameParameter("userId") //login에 필요한 id값으 userId로 설정( default는 username)
.passwordParameter("password") //login에 필요한 password 값을 password(default)로 설정
.defaultSuccessUrl("/") //login에 성공하면 /로 redirect
.successHandler(new UserLoginSuccessHandler()); // 로그인 성공 후 해당 클래스 호출하여 세션에 로그인 정보 담기
}
/*
* 비밀번호 암호화를 위해 사용한다.
*/
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
[ AuthProvider.java ]
src\main\java\kr\co\values\security\AuthProvider.java
로그인 인증을 담당하는 클래스로 화면에서 전달받은 아이디를 통해 mybatis를 활용하여 사용자 정보를 조회 후 비밀번호가 일치하는지 체크하는 로직을 담고 있다.
package kr.co.values.security;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import kr.co.values.login.domain.User;
import kr.co.values.login.service.UserService;
@Component
public class AuthProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String id = (String) authentication.getPrincipal(); // 로그인 창에 입력한 id
String password = (String) authentication.getCredentials(); // 로그인 창에 입력한 password
PasswordEncoder passwordEncoder = userService.passwordEncoder();
UsernamePasswordAuthenticationToken token;
Map<String, Object> map = new HashMap<String, Object>();
map.put("userId", id);
// 화면에서 입력받은 아이디를 통해 사용자 정보가 있는지 조회한다.
User user = userService.getUserById(map);
// System.out.println("user" + user.getUserEmail());
if(user == null) {
throw new UsernameNotFoundException("No User Id");
}
if (user != null && passwordEncoder.matches(password, user.getUserPwd())) { // 일치하는 user 정보가 있는지 확인
List<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority("ROLE_" + user.getUserAuth())); // 권한 부여
token = new UsernamePasswordAuthenticationToken(user, null, roles);
// 인증된 user 정보를 담아 SecurityContextHolder에 저장되는 token
return token;
}
throw new BadCredentialsException("No such user or wrong password.");
// Exception을 던지지 않고 다른 값을 반환하면 authenticate() 메서드는 정상적으로 실행된 것이므로 인증되지 않았다면 Exception을 throw 해야 한다.
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
[ UserLoginSuccessHandler.java ]
src\main\java\kr\co\values\security\UserLoginSuccessHandler.java
로그인 성공했을 때 타는 클래스로 인증된 데이터를 세션에 담는다.
package kr.co.values.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import kr.co.values.login.domain.User;
//스프링 시큐리티를 통한 로그인 접속이 ^성공^ 한 경우 해당 클래스를 호출
public class UserLoginSuccessHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// 로그인 인증이 성공한 이후
// 접속한 로그인 정보 중 아이디, 이름, 이메일을 세션에 담아
// 화면에서 jstl을 통해 세션 정보를 가져와서 사용할 것이다.
HttpSession session = request.getSession(true);
if(session == null) {
session = request.getSession();
}
User userInfo = (User)authentication.getPrincipal();
session.setAttribute("userId", userInfo.getUserId());
session.setAttribute("userName", userInfo.getUserName());
session.setAttribute("userEmail", userInfo.getUserEmail());
response.sendRedirect(request.getContextPath() + "/"); // localhost:8080 + "/"
}
}
- 시큐리티 설정이 끝났으면
Kr\co\values\init\WebConfig.java 파일에 SecurityConfig.class 를 추가해준다.
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {RootConfig.class, SecurityConfig.class};
}
다음으로는 컨트롤러를 비롯한 클래스 파일들을 작성한다.
최종적으로 아래와 같은 패키지 구조로 클래스가 작성될 것이다.

[ 컨트롤러 구현 ]
kr\co\values\login\web\LoginController.java
로그인 프로세스 url로 들어온 경우 앞서 작성한 Authentication 클래스 파일의 시큐리티 설정을 통해 로그인 체크가 이루어진 다음에 로그인이 정상적으로 성공할 경우 메인 ("/")로 이동하도록 설정해주었다.
package kr.co.values.login.web;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
// 사용자가 브라우저를 통해 /login url로 접속했을때 타는 컨트롤러(get방식)
// 주의 : cLogin.jsp에서 post방식으로 호출되는 것은 SecurityConfig에서 .loginProcessingUrl("/login") //Post방식 <-- 이부분으로 인해 시큐리티 AuthProvider를 타게됨!
@GetMapping("/login")
public String loginPage(Model model) {
// 로그인되지 않은 상태이면 로그인 페이지를, 로그인된 상태이면 메인 페이지를 보여줌
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication instanceof AnonymousAuthenticationToken) {
return "cLogin";
}
return "redirect:/";
}
}
[ 서비스 구현 ]
인증 처리를 담당하는 부분에서 호출할 서비스 클래스 및 메소드를 구현하여 준다.
사용자 아이디를 통해 데이터베이스에서 사용자 정보가 있는지 조회하는 로직을 담고 있다.
kr\co\values\login\service\UserService.java
package kr.co.values.login.service;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import kr.co.values.login.domain.User;
import kr.co.values.login.persistence.UserMapper;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
public User getUserById(Map<String, Object> map) {
return userMapper.getUserById(map);
}
public PasswordEncoder passwordEncoder() {
return this.passwordEncoder;
}
}
[ 매퍼 인터페이스 및 쿼리 작성 ]
kr\co\values\login\persistence\UserMapper.java
package kr.co.values.login.persistence;
import java.util.Map;
import kr.co.values.login.domain.User;
public interface UserMapper {
// 사용자 정보 가져오기
User getUserById(Map<String, Object> map);
}
kr\co\values\login\persistence\UserMapper.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="kr.co.values.login.persistence.UserMapper">
<!-- 회원 정보 가져오기 -->
<select id="getUserById" parameterType="hashmap" resultType="kr.co.values.login.domain.User">
SELECT USER_ID
, USER_PWD
, USER_NAME
, USER_EMAIL
, CASE WHEN USER_AUTH = '1' THEN 'USER' ELSE 'ADMIN' END AS USER_AUTH
, USER_TEL
, USER_EMAIL
FROM TBL_USER_INFO
WHERE USER_ID = #{userId}
</select>
</mapper>
[ 모델빈 (도메인) 클래스 작성 ]
- 사용자 정보를 담기 위한 빈(bean) 클래스
- 이미 전 챕터에서 모델빈을 이용하였기때문에 작성했으므로 추가 작성할 필요는 없다.
kr\co\values\login\domain\User.java
package kr.co.values.login.domain;
/*
* 사용자 테이블 컬럼 구조
* `USER_ID` varchar(20) NOT NULL,
`USER_PWD` varchar(100) NOT NULL,
`USER_NAME` varchar(100) DEFAULT NULL,
`USER_AUTH` char(1) DEFAULT NULL,
`USER_TEL` varchar(20) DEFAULT NULL,
`USER_EMAIL` varchar(50) DEFAULT NULL,
`INPUT_DATETIME` datetime DEFAULT NULL,
*/
public class User {
private String userId;
private String userPwd;
private String userName;
private String userAuth;
private String userTel;
private String userEmail;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserAuth() {
return userAuth;
}
public void setUserAuth(String userAuth) {
this.userAuth = userAuth;
}
public String getUserTel() {
return userTel;
}
public void setUserTel(String userTel) {
this.userTel = userTel;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
}
[ 로그인 테스트 ]
/login 으로 접속하여 로그인 테스트를 진행한다.
로그인시 메인페이지로 정상 이동했다면 정상작동!
< 추가 >
- incJs.jsp 상단에는 UTF-8 설정을 통해 한글 저장이 가능하도록 아래 코드도 입력하여 준다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
- 헤더 배경이미지 바꾸기
1. 마음에 드는 이미지를 선택후 bg_head2.jpg 이름으로 저장한다.
2. myPortfolio\src\main\webapp\resources\images 폴더 아래에 bg_head2.jpg 파일을 붙여넣는다.
3. myPortfolio\src\main\webapp\resources\css\styles.css
위 파일의 228라인의 스타일 정보를 아래와 같이 수정한다.
#head {
background: #f4f4f4 url(../images/bg_head2.jpg) top center;
background-size: cover;
color: #7C7C7C;
padding: 30px 0 35px 0;
}
#head img.img-circle {
display: block;
width: 140px;
height: 140px;
overflow: hidden;
border: 9px solid rgba(0, 0, 0, 0.05);
margin: 0 auto;
}
#head .title {
font-family: Alice, Georgia, serif;
font-size: 49px;
font-size: 3.0625rem;
}
#head .title a {
text-decoration: none;
color: #333333;
}
#head .tagline {
display: block;
font-size: 14px;
font-size: 0.875rem;
line-height: 1.2em;
color: #333;
margin: 5px 0 0;
font-weight: bold;
text-shadow: 2px -2px 2px #eef1ec;
}
#head .tagline b {
font-weight: normal;
}
#head .tagline a {
color: #333333;
}
.home #head {
padding: 90px 0;
}
.home #head .title {
font-size: 49px;
font-size: 3.0625rem;
color: #0c0c0c;
text-shadow: 2px -2px 2px #f5f5f5;
}
.home #head .tagline {
font-size: 16px;
font-size: 1rem;
margin: 15px 0 0;
}

'Spring MyPortfolio Project' 카테고리의 다른 글
[포트폴리오 프로젝트 7] Q & A 게시판 구현 (0) | 2024.01.24 |
---|---|
[포트폴리오 프로젝트 6] 관리자메뉴 - 사용자관리 페이지 구현 (0) | 2024.01.24 |
[포트폴리오 프로젝트 4] 회원가입 페이지 구현 (0) | 2024.01.24 |
[포트폴리오 프로젝트 3] 관리자 페이지 구현 - 프로필 업데이트 (2) | 2024.01.24 |
[포트폴리오 프로젝트 2] 메인페이지 구현 : 캐러셀 사용 (2) | 2024.01.24 |