CommonController.java 일부
package net.developia.spring06.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.extern.log4j.Log4j;
@Controller
@Log4j
public class CommonController {
@GetMapping("/accessError")
public void accessDenied(Authentication auth, Model model) {
log.info("acess Denied: " + auth);
model.addAttribute("msg", "Access Denied");
}
@GetMapping("/customLogin")
public void loginInput(String error, String logout, Model model) {
log.info("error: " + error);
log.info("logout: " + logout);
if (error != null) {
model.addAttribute("error", "Login Error Check Your Account");
}
if (logout != null) {
model.addAttribute("logout", "Logout!!");
}
}
}
customLogin.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html14/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Custom Login Page</h1>
<h2><c:out value="${error}"/></h2>
<h2><c:out value="${logout}"/></h2>
<form method='post' action="/login">
<div>
<input type='text' name='username' value='admin'>
</div>
<div>
<input type='password' name='password' value='admin'>
</div>
<div>
<input type='submit'>
</div>
<input type='hidden' name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</body>
</html>
CustomLoginSuccessHandler.java
package net.developia.spring06.security;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;
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.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import lombok.extern.log4j.Log4j;
@Log4j
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException {
log.warn("Login Success");
List<String> roleNames = new ArrayList<>();
auth.getAuthorities().forEach(authority -> {
roleNames.add(authority.getAuthority());
});
log.warn("ROLE NAMES: " + roleNames);
if(roleNames.contains("ROLE_ADMIN")) {
response.sendRedirect("/sample/admin");
return;
}
response.sendRedirect("/");
}
}
security-context.xml 일부
<security:http>
<security:intercept-url pattern="/sample/all" access="permitAll"/>
<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/sample/all" access="permitAll"/>
<!-- <security:access-denied-handler error-page="/accessError"/> -->
<security:access-denied-handler ref="customAccessDenied"/>
<!-- <security:form-login/> -->
<!-- <security:form-login login-page="/customLogin" /> -->
<security:form-login login-page="/customLogin" authentication-success-handler-ref="customLoginSuccess" />
</security:http>
security-context.xml 일부
<security:logout logout-url="/customLogout" invalidate-session="true"/>
CommonController.java 일부
@GetMapping("/customLogout")
public void logoutGET() {
log.info("custom logout");
}
customLogout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html14/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1> Logout Page </h1>
<form action="/customLogout" method='post'>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button>로그아웃</button>
</form>
</body>
</html>
admin.jsp 일부
<body>
<h1>/sample/admin page</h1>
<a href="/customLogout">Logout</a>
</body>
ace.sql
create table users(
username varchar2(50) not null primary key,
password varchar2(50) not null,
enabled char(1) default '1');
create table authorities (
username varchar2(50) not null,
authority varchar2(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
insert into users (username, password) values ('user00','pw00');
insert into users (username, password) values ('member00','pw00');
insert into users (username, password) values ('admin00','pw00');
insert into authorities (username, authority) values ('user00','ROLE_USER');
insert into authorities (username, authority) values ('member00','ROLE_MANAGER');
insert into authorities (username, authority) values ('admin00','ROLE_MANAGER');
insert into authorities (username, authority) values ('admin00','ROLE_ADMIN');
commit;
select * from users;
select * from authorities order by authority;
CustomNoOpPasswordEncoder.java
package net.developia.spring06.security;
import org.springframework.security.crypto.password.PasswordEncoder;
import lombok.extern.log4j.Log4j;
@Log4j
public class CustomNoOpPasswordEncoder implements PasswordEncoder {@Override
public String encode(CharSequence rawPassword) {
log.warn("before encode :" + rawPassword);
return rawPassword.toString();
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
log.warn("marches: " + rawPassword + ":" + encodedPassword);
return rawPassword.toString().equals(encodedPassword);
}
}
security-context.xml
<?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-2.0.xsd">
<bean id="customAccessDenied" class="net.developia.spring06.security.CustomAccessDeniedHandler"></bean>
<bean id="customLoginSuccess" class="net.developia.spring06.security.CustomLoginSuccessHandler"></bean>
<bean id="customPasswordEncoder" class="net.developia.spring06.security.CustomNoOpPasswordEncoder"></bean>
<!-- <security:http auto-config="true" use-expressions="false"> -->
<security:http>
<security:intercept-url pattern="/sample/all" access="permitAll"/>
<security:intercept-url pattern="/sample/member" access="hasRole('ROLE_MEMBER')"/>
<security:intercept-url pattern="/sample/admin" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/sample/all" access="permitAll"/>
<!-- <security:access-denied-handler error-page="/accessError"/> -->
<security:access-denied-handler ref="customAccessDenied"/>
<!-- <security:form-login/> -->
<!-- <security:form-login login-page="/customLogin" /> -->
<security:form-login login-page="/customLogin" authentication-success-handler-ref="customLoginSuccess" />
<security:logout logout-url="/customLogout" invalidate-session="true"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"/>
<security:password-encoder ref="customPasswordEncoder"/>
<!-- <security:user-service>
<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
</security:user-service> -->
</security:authentication-provider>
</security:authentication-manager>
</beans>
ace.sql
create table tbl_member(
userid varchar2(50) not null primary key,
userpw varchar2(100) not null,
username varchar2(100) not null,
regdate date default sysdate,
updatedate date default sysdate,
enabled char(1) default '1');
create table tbl_member_auth (
userid varchar2(50) not null,
auth varchar2(50) not null,
constraint fk_member_auth foreign key(userid) references tbl_member(userid)
);
pom.xml 일부
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
MemverTests.java
package net.developia.spring06.security;
import java.sql.Connection;
import java.sql.PreparedStatement;
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;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src//main/webapp/WEB-INF/spring/security-context.xml"
})
@Log4j
public class MemberTests {
@Setter(onMethod_ = @Autowired)
private PasswordEncoder pwencoder;
@Setter(onMethod_ = @Autowired)
private DataSource ds;
@Test
public void testInsertMemeber() {
String sql = "insert into tbl_member(useid, userpw, username) values (?,?,?)";
for(int i = 0; i < 100; i++) {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = ds.getConnection();
pstmt = con.prepareStatement(sql);
// pstmt.setString(2, pwencoder.encode("pw" + i));
if(i < 80) {
pstmt.setString(1, "user"+i);
pstmt.setString(2, "ROLE_USER");
} else if (i < 90) {
pstmt.setString(1, "manager"+i);
pstmt.setString(2, "ROLE_USER");
} else {
pstmt.setString(1, "admin"+i);
pstmt.setString(2, "ROLE_USER");
}
pstmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
} finally {
if(pstmt != null) { try {pstmt.close(); } catch (Exception e) {} }
if(con != null) {try { con.close(); } catch(Exception e) {} }
}
} //end for
}
}
ace.sql
select
userid username, userpw password, enabled
from
tbl_member
where userid = 'admin90';
select
userid username, auth authority
from
tbl_member_auth
where userid = 'admin90';
security-context.xml 일부
<security:authentication-manager>
<security:authentication-provider>
<!-- <security:jdbc-user-service data-source-ref="dataSource"/> -->
<security:jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="select userid, userpw, enabled from tbl_member where userid = ? "
authorities-by-username-query="select userid, auth from tbl_member_auth where userid = ? " />
<!-- <security:password-encoder ref="customPasswordEncoder"/> -->
<security:password-encoder ref="bcryptPasswordEncoder" />
<!-- <security:user-service>
<security:user name="member" password="{noop}member" authorities="ROLE_MEMBER"/>
<security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
</security:user-service> -->
</security:authentication-provider>
</security:authentication-manager>
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'me.choijeongyun'
version = '1.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
SpringBootDeveloperApplication.java
package org.example.springbootdeveloper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootDeveloperApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDeveloperApplication.class, args);
}
}
TestController.java
package org.example.springbootdeveloper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "Hello, world!";
}
}
http://localhost:8080/test 실행화면
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'org.exmaple'
version = '1.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-web'
// testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:sping-boot-starter-data-jap'
runtimeOnly 'com.h2database:h2' // 인메모리 데이터베이스
compileOnly 'org.projectlombok:lombok' // 롬복
annotationProcessor 'org.projectlombok:lombok'
}
test {
useJUnitPlatform()
}
TestController.java
package org.example.springbootdeveloper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Member;
@RestController
public class TestController {
@Autowired // TestService 빈 주입
TestService testService;
@GetMapping("/test")
public List<Member> getAllMembers() {
List<Member> members = testService.getAllMembers();
return members;
}
}
TestService.java
package org.example.springbootdeveloper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TestService {
@Autowired
MemberRepository memberRepository;
public List<Member> getAllMembers() {
return memberRepository.findAll();
}
}
Member.java
package org.example.springbootdeveloper;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Getter
@Entity
public class Member {
@Id
@GenratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id; // DB 테이블의 'id' 컬럼과 매칭
@Column(name = "name", nullable = false)
private String name; // DB 테이블의 'name' 컬럼과 매칭
}
MemberRepository.java
package org.example.springbootdeveloper;
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
}
data.sql
INSERT INTO member (id, name) VALUES (1, 'name 1')
INSERT INTO member (id, name) VALUES (2, 'name 2')
INSERT INTO member (id, name) VALUES (3, 'name 3')