・스프링 시큐리티(인증과 권한) = 인증과 권한을 위한 솔루션(프레임워크)
[*스프링 시큐리티 실행 순서]
1. pom.xml에 생성하자마자 스프링 시큐리티 객체 생성
2. Request객체를 낚아챔(Filter 기능 @EnableWebSecurity) : SecurityConfig.java 실행하여 URL체크 → 로그인 화면으로 넘어감 → 로그인까지
3. 인증 완료 후 Controller로 넘어감
[*스프링 시큐리티 특징과 구조]
- 보안과 관련하여 체계적으로 많은 옵션을 제공하여 편리하게 사용할 수 있음
- Filter기반으로 동작하여 MVC와 분리하여 관리 및 동작
- 어노테이션을 통한 간단한 설정 Spring Security는
- 기본적으로 세션 & 쿠키방식으로 인증
1) 인증과 권한
- 인증(Authentication)=로그인
* 인증 : 푸르덴셜타워 출입 시 사원증 찍음
- 권한(인가)(Authorization) = 리소스 접근 제어
* 권한(인가) : 각 부서에 출입 시 사원증 찍음
2) 암호화 모듈 제공
3) CSRF 방어 모듈 제공
・인증API
http.formLogin()
http.logout()
http.csrf()
http.httpBasic()
・인가API
http.authorizeRequests()
.antMatchers(/admin)
.hasRole(User)
- 접근 주체(Principal) : 보호된 리소스에 접근하는 대상 || 일반 유저
- 인증(Authentication) : 보호된 리소스에 접근한 대상이 누구인지
- 인가(Authorize) : 해당 리소스에 대해 접근 가능한 권한을 가지고 있는지 확인하는 과정(After Authentication, 인증 이후)
- 권한
・시큐리티 테이블
user(1)유저이름 : authorities권한이름(N)
시큐리티 파일 2개 줄테니까
1. UserDetailsVO : user이름, password, 권한을 적어라
2. CustomUserDetailsService > SecurityConfig에서 받아옴
*Junit Test(UserMapper.java에서 오른쪽 클릭)
[계산 pom.xml] <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.8</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>edu.global</groupId> <artifactId>ex</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring_boot_kdb_security</name> <description>Board project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <repositories> <repository> <id>oracle</id> <url>http://www.datanucleus.org/downloads/maven2/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring-boot-devtools는 클래스 수정시 웹서버를 재시작하여 결과를 바로 반영 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 오라클 JDBC 드라이버 --> <dependency> <groupId>oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3</version> </dependency> <!-- MyBatis 라이브러리 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <!-- MyBatis sql pretty --> <dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId> <version>1.16</version> </dependency> <!-- JSP를 사용하기 위한 라이브러리 --> <!-- 톰캣 파서 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- jstl 라이브러리 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> </dependencies> <build><!-- 컴파일~배포까지 --> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <url>http://146.56.137.240:8282/manager/text</url> <username>admin</username> <password>1234</password> </configuration> </plugin> <!-- cmd에 입력 ( 배포 ) : mvnw.cmd tomcat7:redeploy --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> --------------------------------------------------------------------- [계산 application.properties] #server port number server.port = 8282 #datasource (oracle) #spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver #spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy spring.datasource.url=jdbc:log4jdbc:oracle:thin:@localhost:1521/xe spring.datasource.username=scott spring.datasource.password=tiger spring.devtools.livereload.enabled=true #MyBatis #xml location #src/main/resources/mappers mybatis.mapper-locations=classpath:mappers/`**/*.xml mybatis.type-aliases-package=edu.global.ex #jsp spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp #logging #logging.level.root=info --------------------------------------------------------------------- [계산 HomeController.java] package edu.global.ex.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller public class HomeController { @GetMapping("/") public String home() { return "home"; } @GetMapping("/user/userHome") public void userHome() { // void는 return값이 없지만, userHome.jsp를 리턴함 log.info("userHome ..."); } //상기 userHome함수는 하기와 동일한 의미이다. // @GetMapping("/user/userHome") // public String userHome() { // log.info("userHome ..."); // return "user/userHome"; // } @GetMapping("/admin/adminHome") public void adminHome() { log.info("adminHome ..."); } } --------------------------------------------------------------------- [계산 SecurityConfig.java] package edu.global.ex.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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; @Configuration // @Component + 의미(설정할 수 있는 파일)//(객체 생성해서 IOC컨테이너에 넣어라) @EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록됨 = 스프링 시큐리티를 작동시키는 파일이라는 것을 알려줌 - 스프링한테. public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService customUserDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { // 우선 CSRF설정을 해제한다. // 초기 개발시만 해주는게 좋다. http.csrf().disable(); http.authorizeRequests() // .antMatchers("/user/**").hasAnyRole("USER") // .antMatchers("/admin/**").hasAnyRole("ADMIN") // .antMatchers("/**").permitAll(); .antMatchers("/**").hasAnyRole("ADMIN"); // id: admin, pw:admin http.formLogin(); // 스프링 시큐리티에 있는 기본 로그인 폼을 사용하겠다. } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("{noop}user").roles("USER").and().withUser("admin") .password("{noop}admin").roles("ADMIN"); } } --------------------------------------------------------------------- [계산 UserVO.java] package edu.global.ex.vo; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public class UserVO { private String username; private String password; private int enabled; private List<AuthVO> authList; } --------------------------------------------------------------------- [계산 AuthVO.java] package edu.global.ex.vo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public class AuthVO { private String username; private String authority; } --------------------------------------------------------------------- [계산 UserMapper.java] package edu.global.ex.mapper; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import edu.global.ex.vo.UserVO; @Mapper // MyBatis용 인터페이스라는 것을 알려주는 어노테이션 public interface UserMapper { public UserVO getUser(String username); @Insert("insert into users(username,password,enabled) values(#{username},#{password},#{enabled})") public int insertUser(UserVO userVO); @Insert("insert into AUTHORITIES (username,AUTHORITY) values(#{username},'ROLE_USER')") public void insertAuthorities(UserVO UserVO); } --------------------------------------------------------------------- [계산 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="edu.global.ex.mapper.UserMapper"> <resultMap id="userMap" type="UserVO"> <result property="username" column="username" /> <result property="password" column="password" /> <result property="enabled" column="enabled" /> <collection property="authList" resultMap="authMap"></collection> </resultMap> <resultMap id="authMap" type="AuthVO"> <result property="username" column="username" /> <result property="authority" column="authority" /> </resultMap> <select id="getUser" resultMap="userMap"> <!-- 조인 처리 --> select * from users , authorities where users.username = authorities.username and users.username = #{username} </select> </mapper> --------------------------------------------------------------------- [계산 UserDetailsVO.java] package edu.global.ex.vo; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class UserDetailsVO implements UserDetails { // DB와 연결 || ID/PW/Authority를 가져와 연결 private String username; // ID private String password; // PW private List<GrantedAuthority> authorities; public UserDetailsVO(UserVO user) { this.setAuthorities(user); this.setPassword(user.getPassword()); this.setUsername(user.getUsername()); } // setter public void setUsername(String username) { this.username = username; } // setter public void setPassword(String password) { this.password = password; } // setter public void setAuthorities(UserVO userVO) { List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (AuthVO auth : userVO.getAuthList()) { authorities.add(new SimpleGrantedAuthority(auth.getAuthority())); } this.authorities = authorities; } // ============================================================================== @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } // 계정이 만료되지 않았는가? @Override public boolean isAccountNonExpired() { return true; } // 계정이 잠기지 않았는가? @Override public boolean isAccountNonLocked() { return true; } // 패스워드가 만료되지 않았는가? @Override public boolean isCredentialsNonExpired() { return true; } // 계정이 활성화 되었는가? @Override public boolean isEnabled() { return true; } } --------------------------------------------------------------------- [계산 CustomUserDetailsService.java] package edu.global.ex.security; 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 org.springframework.stereotype.Service; import edu.global.ex.mapper.UserMapper; import edu.global.ex.vo.UserDetailsVO; import edu.global.ex.vo.UserVO; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.warn("Load User By UserVO number: " + username); UserVO vo = userMapper.getUser(username); log.warn("queried by UserVO mapper: " + vo); return vo == null ? null : new UserDetailsVO(vo); } } --------------------------------------------------------------------- [계산 UserMapperTest.java] : Junit Test(UserMapper.java 우측 클릭) package edu.global.ex.mapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import edu.global.ex.vo.UserVO; import lombok.extern.slf4j.Slf4j; @Slf4j @SpringBootTest class UserMapperTest { @Autowired private UserMapper userMapper; @Test void testInserUser() { // @Insert("insert into users(username,password,enabled) values(#{username},#{password},#{enabled})") // public int insertUser(UserVO userVO); // // @Insert("insert into AUTHORITIES (username,AUTHORITY) values(#{username},'ROLE_USER')") // public void insertAuthorities(UserVO UserVO); UserVO user = new UserVO(); user.setUsername("kim2"); user.setPassword(new BCryptPasswordEncoder().encode("kim2")); user.setEnabled(1); userMapper.insertUser(user); userMapper.insertAuthorities(user); } } --------------------------------------------------------------------- [계산 home.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> <img src="/img/bye.gif"> </body> </html> --------------------------------------------------------------------- [계산 adminHome.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> <h1>관리자(어드민) 페이지입니다.</h1> </body> </html> --------------------------------------------------------------------- [계산 userHome.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> <h1>유저 페이지입니다.</h1> </body> </html>
[결과값]
localhost:8282/user/userHome
localhost:8282/admin/adminHome
시간 순서
구성요소(Component)
1) 시스템(System)
만들고자 하는 프로그램을 나타낸다
2) 액터(Actor)
시스템의 외부에 있고 시스템과 상호작용을 하는 사람(시스템의 기능을 사용하는 사람), 시스템(시스템에 정보를 제공하는 또 다른 시스템)을 말한다.
3) 유스케이스(Usecase)
사용자 입장에서 바라본 시스템의 기능
시스템이 액터에게 제공해야 하는 기능으로 시스템의 요구사항을 나타낸다.
-표기
타원으로 표시하고 안쪽에 유스케이스명을 작성한다.
유스케이스명은 "~한다"와 같이 동사로 표현한다.
4) 관계(Relation)
1)포함 관계(필수적으로 요구) : login된 상태에서만 include표시
2)확장 관계(선택적 관계:특정 조건이 만족될경우) : 글을 첨부하는데 파일을 첨부해도 되고 안 해도 된다.
3)일반화 관계(Generalization) - 추상화