Back-End/Java

[Spring] 스프링 Security 로그인 예제

CJun 2021. 6. 24. 22:52
반응형

 

 

[Spring] 스프링 Security 사용이유와 설정 방법을 알아보자

spring security 란 ? Spring 기반의 애플리케이션의 은증과 권한, 인가 등의 보안을 담당하는 스프링 하위 프레임 워크이다. 처리 방법으로는 Filter 흐름에 따라 처리하고 있으며 특히 Spring Security는 보

soulno.tistory.com

 

JUnit
src/test/java/com.my.com.myapp/
MemberTest.java

 

package com.mycom.myapp;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/root-context.xml",
		"file:src/main/webapp/WEB-INF/spring/security-context.xml" })
public class MemberTest {
	@Autowired
	private PasswordEncoder pwencoder;
	@Autowired
	private DataSource ds;

	//사용자 관련 테이블 
	@Test
	public void testInsertMember() {
		String sql = "insert into tbl_member(userid, userpw, username) values(?,?,?)";
		for (int i = 0; i < 100; i++) {
			Connection con = null;
			PreparedStatement ps = null;

			try {
				con = ds.getConnection();
				ps = con.prepareStatement(sql);
				ps.setString(2, pwencoder.encode("pw" + i));
				if (i < 80) {
					ps.setString(1, "user" + i);
					ps.setString(3, "일반사용자" + i);
				} else if (i < 90) {
					ps.setString(1, "manager" + i);
					ps.setString(3, "운영자" + i);
				} else {
					ps.setString(1, "admin" + i);
					ps.setString(3, "관리자" + i);
				}
				ps.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				if(ps!=null) try {ps.close();} catch (SQLException e) {e.printStackTrace();}
				if(con!=null) try {con.close();} catch (SQLException e) {e.printStackTrace();}
			}
		}
	}// testInserMemebr
	
	//권한 관련 테이블 
	@Test
	public void testInsertAuth() {
		String sql = "insert into tbl_member_auth(userid, auth) values(?,?)";
		for (int i = 0; i < 100; i++) {
			Connection con = null;
			PreparedStatement ps = null;

			try {
				con = ds.getConnection();
				ps = con.prepareStatement(sql);
				if (i < 80) {
					ps.setString(1, "user" +i);
					ps.setString(2, "ROLE_USER");
				} else if (i < 90) {
					ps.setString(1, "manager"+i);
					ps.setString(2, "ROLE_MANAGER");
				} else {
					ps.setString(1, "admin"+i);
					ps.setString(2, "ROLE_ADMIN");
				}
				ps.executeUpdate();
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				if(ps!=null) try {ps.close();} catch (SQLException e) {e.printStackTrace();}
				if(con!=null) try {con.close();} catch (SQLException e) {e.printStackTrace();}
			}
		}
	}
}

※ 여기서 ContextConfiguration 경로 지정을 해주었고

String sql 통해서 Mysql에 데이터를 삽입하였다.

 

JUnit
src/test/java/com.my.com.myapp/
MemberMapperTest.java

 

package com.mycom.myapp;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mysecurity.dto.MemberDTO;
import com.mysecurity.mapper.MemberMapper;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/root-context.xml",
		"file:src/main/webapp/WEB-INF/spring/security-context.xml" })
public class MemberMapperTest {
	
	@Autowired
	private MemberMapper mapper;
	@Test
	public void testRead() {
		MemberDTO member = mapper.read("admin90");
		System.out.println(member);
		member.getAuthList().forEach(authVO -> System.out.println(authVO));
	}
}

 

src/main/java/com/mycom.myapp/
HomeController.java

 

package com.mycom.myapp;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	/**
	 * Simply selects the home view to render by returning its name.
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		logger.info("Welcome home! The client locale is {}.", locale);

		Date date = new Date();
		DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

		String formattedDate = dateFormat.format(date);

		model.addAttribute("serverTime", formattedDate);

		return "home";
	}

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

	@GetMapping("admin")
	public String admin() {
		return "admin";
	}

	@GetMapping("manager")
	public String manager() {
		return "manager";
	}

	@GetMapping("customLogout")
	public String customLogout() {
		return "customLogout";
	}

	@GetMapping("all")
	public String all() {
		return "all";
	}
}

 

src/main/java/com/mysecurity/domain/
CustomLoginSuccess.java
package com.mysecurity.domain;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class CustomLoginSuccess implements AuthenticationSuccessHandler {

	@Autowired
	private PasswordEncoder passwordEncoder;
	//비번과 권한 만들기 
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
		
		//비번
		String encPwd = passwordEncoder.encode(request.getParameter("password"));
		System.out.println("password: " + encPwd);
		
		//권한
		List<String> roleNames = new ArrayList<String>();
		authentication.getAuthorities().forEach(authority->roleNames.add(authority.getAuthority()));
		
		System.out.println("roleNames:" + roleNames);
		
		//
		if(roleNames.contains("ROLE_ADMIN")) {
			response.sendRedirect("/myapp/admin");
			return;
		}
		if(roleNames.contains("ROLE_MANAGER")) {
			response.sendRedirect("/myapp/manager");
			return;
		}
		response.sendRedirect("/myapp");
		}
}

 

src/main/java/com/mysecurity/domain/
CustomUser.java
package com.mysecurity.domain;

import java.util.stream.Collectors;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import com.mysecurity.dto.MemberDTO;

import lombok.Getter;
import lombok.Setter;


@Getter @Setter
public class CustomUser extends User { //상속을 받거나 implements하거나 상관없음
	private MemberDTO member;
	public CustomUser(MemberDTO member) {
		super(member.getUserid(),
				member.getUserpw(),
				member.getAuthList().stream().map(auth->new SimpleGrantedAuthority(auth.getAuth()))
				.collect(Collectors.toList()) //반복문
		);
		this.member = member; 
	}
}

 

src/main/java/com/mysecurity/domain/
CustomUserDetailService.java
package com.mysecurity.domain;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.mysecurity.dto.MemberDTO;
import com.mysecurity.mapper.MemberMapper;

public class CustomUserDetailsService implements UserDetailsService {
	@Autowired
	private MemberMapper memberMapper;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("loadUserByUsername");
		MemberDTO member = memberMapper.read(username);
		return member == null ? null : new CustomUser(member);
	}
}

 

src/main/java/com/mysecurity/dto/
AuthDTO.java
package com.mysecurity.dto;

import lombok.Data;

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

 

src/main/java/com/mysecurity/dto/
MemberDTO.java
package com.mysecurity.dto;

import java.util.List;

import lombok.Data;

@Data
public class MemberDTO {
	//DB테이블과 같게 모델링
	private String userid;
	private String userpw;
	private String username;
	private boolean enabled;
	private String regDate;
	private String updateDate;
	private List<AuthDTO> authList;
}

 

src/main/java/com/mysecurity/mapper/
MemberMapper.java
package com.mysecurity.mapper;

import com.mysecurity.dto.MemberDTO;

public interface MemberMapper {
	public MemberDTO read(String userid);
	
}

 

 

src/main/resources/com/mysecurity/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 가 어떤 파일과 같이 연동되서 쓰일지 보여준다 -->
<mapper namespace="com.mysecurity.mapper.MemberMapper">
<resultMap type="com.mysecurity.dto.MemberDTO" id="memberMap">
<id property="userid" column="userid"/>
<result property="userid" column="userid"/>
<result property="userpw" column="userpw"/>
<result property="username" column="username"/>
<result property="regDate" column="regdate"/>
<result property="updateDate" column="updatedate"/>
<collection property ="authList" resultMap="authMap"/>
</resultMap>

<resultMap type="com.mysecurity.dto.AuthDTO" id="authMap">
	<result property="userid" column="userid"/>
	<result property="auth" column="auth"/>
</resultMap>

<select id="read" resultMap="memberMap">
select mem.userid, userpw, username, regdate, updatedate, auth
from tbl_member mem
left outer join tbl_member_auth auth
on 
mem.userid = auth.userid
where mem.userid=#{userid}
</select>
</mapper>

 

home.jsp
<%@ 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" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	Hello world!  
</h1>
<!-- 권한에따라 화면 여부 
isAnonymous : 누구나
isAuthenticated : 권한
-->
<sec:authorize access="isAnonymous()">
<a href="customLogin">로그인</a>
<a href="/myapp/board/list">게시판</a>
<a href="/myapp/board/insert">게시판 글쓰기</a>
</sec:authorize>
<sec:authorize access="isAuthenticated()">
<a href="customLogout">로그아웃</a>
</sec:authorize>
</body>
</html>

 

login.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>Login</title>
</head>
<body>
<form method="post" action="/myapp/login">
ID: <input type="text" name="username"><br>
PWD: <input type="password" name="password"><br>
<input type="submit" value="로그인전송"><br>
</form>
</body>
</html>

 

customLogout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/myapp/customLogout" method="post">
<button>로그아웃</button>
</form>
</body>
</html>

 

반응형