Spring Security

Jiwon Park·2023년 3월 14일
0

Setting

1. pom.xml 의존성 라이브러리 주입(maven repository)

spring-security-web
spring-security-config
spring-security-core
spring-security-taglibs

<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-web</artifactId>
		    <version>5.7.5</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-config</artifactId>
		    <version>5.7.5</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-core</artifactId>
		    <version>5.7.5</version>
		</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
		<dependency>
		    <groupId>org.springframework.security</groupId>
		    <artifactId>spring-security-taglibs</artifactId>
		    <version>5.4.6</version>
		</dependency>

2. web.xml : param추가

<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml
					/WEB-INF/spring/security-context.xml
		</param-value>
	</context-param>

filter추가

<!-- security filter -->
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>
			org.springframework.web.filter.DelegatingFilterProxy
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>코드를 입력하세요

3.security-context.xml 작성 - 위치 spring 바로 아래

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
		     xmlns:beans="http://www.springframework.org/schema/beans"
		     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		     xsi:schemaLocation="http://www.springframework.org/schema/beans
								 http://www.springframework.org/schema/beans/spring-beans.xsd
								 http://www.springframework.org/schema/security
								 http://www.springframework.org/schema/security/spring-security.xsd">

<!--     <http auto-config="true" 기본 로그인 창 사용시 use-expressions="true">  -->
    <http use-expressions="true"> 
	<intercept-url pattern="/cars/add/**" access="hasAuthority('USER_MANAGER')" />
	<intercept-url pattern="/**" access="permitAll" />
 	<form-login login-page="/login"
				default-target-url="/cars"
				authentication-failure-url="/loginfailed"
				username-parameter="username"
				password-parameter="password" />
	<csrf/>
	<logout logout-url = "/logout" logout-success-url = "/login" invalidate-session="true"/>
    </http> 

	<!-- 암호화를 위한 passwordEncoder -->
	<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
	
	<!-- DB연동은 data_source만 지정해주면 된다, 테이블이름은 정확히.   users 랑 authorities -->
    <authentication-manager alias="authenticationManager"> 
        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"/>
            <password-encoder ref="passwordEncoder"/>
        </authentication-provider>
    </authentication-manager>
    
   


post로 요청할때 토큰을 포함시키지 않으면 403에러가 발생한다.
보안 토큰을 안보낼거면 <security:csrf disabled="true"/> 을 통해 비활성화를 해야 post로 요청이 가능하다.

<input type = "hidden" name = "${_csrf.parameterName }" value = "${_csrf.token }">

{noop} // 비밀번호 암호화 안할때 임시로 쓰기

Spring Security에서 Role의 경우에는 prefix로 "ROLE_" 이라는 문자를 자동으로 붙인다. 

  1. hasRole("USER") == hasAuthority("ROLE_USER")
  2. hasRole은 DB상 role 정보에 prefix로 "ROLE_"을 붙여주면 된다.

use-expressions="true” (default값이 true) //false이면 SpEL을 사용하지 않고 일반적인 방법으로 작성

false -> access="ROLE_ANONYMOUS”

true -> access=”hasAuthority('ROLE_ANONYMOUS')”


4. DataBase

BCrypt 암호화는 디비의 password를 char(60)로 하는 것이 좋다.
고정된 문자열 크기는 varchar보다 char 타입이 빠르게 작동한다.

CREATE TABLE `users` (
	`uno` INT(10) NOT NULL AUTO_INCREMENT,
	`username` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',
	`password` CHAR(60) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',
	`enabled` TINYINT(1) NOT NULL DEFAULT '1',
	`uname` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_0900_ai_ci',
	`uemail` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_0900_ai_ci',
	PRIMARY KEY (`username`) USING BTREE,
	INDEX `uno` (`uno`) USING BTREE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
AUTO_INCREMENT=10
;




CREATE TABLE `authorities` (
	`username` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_0900_ai_ci',
	`authority` VARCHAR(50) NOT NULL DEFAULT 'USER' COLLATE 'utf8mb4_0900_ai_ci',
	UNIQUE INDEX `ix_auth_username` (`username`, `authority`) USING BTREE,
	CONSTRAINT `FK_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8mb4_0900_ai_ci'
ENGINE=InnoDB
;


* Service클래스 Example

@Service
public class UserServiceImpl implements UserService{
	
    @Autowired
	private BCryptPasswordEncoder pwEncoder;
    
	private UserRepository userRepository;
    
    //(쌩성자 주입) 단일 생성자 @Autowired 생략 가능
	public UserServiceImpl(UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	

	@Override
	public boolean join(User user) {
		
		user.setPassword(pwEncoder.encode(user.getPassword())); //암호화
		return (userRepository.insertInfo(user) == 1) && userRepository.insertAuth(user.getUsername()) == 1
				? true : false;
	}


	
}



//컨트롤러

@Controller
public class MemberController {

	
	private UserService userService;
	
    //생성자 주입방식(단일 생성자 autowired 생략 가능)
	public MemberController(UserService userService) {
		this.userService = userService;
	}

	@GetMapping("/join")
	public String joinView() {return "join";}
	
	@PostMapping("/join")	
	public String join(@ModelAttribute User user) {		
		if(userService.join(user)) {
			return "redirect:/login";
		}
		return "redirect:/join";
	}
	
	@GetMapping("/login")
	public String login() {return "login";}
	
	@GetMapping("/loginFailed")
	public String logoutFailed() {return "login";}
	
profile
안녕하세요

0개의 댓글