SpringBoot
1. 인증
- 정상적인 접근인지 비정상적인 접근인지 구분
2. 인가
- 등급에 따른 접근을 제어
EX)
- 사원 권한
- 관리자 권한
- 손님 권한
<!-- security 추가하기 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
pom.xml에 해당 내용을 추가하여 스프링부트 시큐리티를 사용하도록 선언한다.
Spring Security를 체크하여 사용하도록 한다.
시큐리티 연결
package com.psd.k4.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 시큐리티에서 사용하는 데 필요한 수많은 객체를 생성한다.
public class SecurityConfig {
SecurityConfig(){
System.out.println("===> 0. SecurityConfig 생성자 확인 ");
}
@Autowired
SecurityUserDetailsService securityUserDetail;
@Bean
SecurityFilterChain filterChain( HttpSecurity http ) throws Exception {
http.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/member/**").authenticated()
.requestMatchers("/manager/**").hasRole("MANAGER")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().permitAll());
http.csrf().disable();
http.formLogin().loginPage("/login.do").defaultSuccessUrl("/loginSuccess.do", true);
http.exceptionHandling().accessDeniedPage("/accessDenied.do");
http.logout().invalidateHttpSession(true).logoutSuccessUrl("/");
http.userDetailsService(securityUserDetail);
return http.build();
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
URL에 포함된 권한이 있을시 해당 권한을 불러온다.
시큐리티 유저 (DB에 아이디 패스워드 비교)
package com.psd.k4.security;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import com.psd.k4.login.LoginVO;
// User 는 폼의 username 과 password 와 자동매칭 진행
public class SecurityUser extends User {
private static final long serialVersionUID = 1L;
// SecurityUserDetailsService 에서 넘어온 table의 username 과 password
// 를 비교하고 있다고 이해를 하면된다.!!!
public SecurityUser( LoginVO vo ) {
super(vo.getUsername() , "{noop}"+vo.getPassword(),
AuthorityUtils.createAuthorityList(vo.getRole().toString()));
System.out.println("SecurityUser 확인:" + vo.getUsername() + " : " + vo.getPassword());
}
}
{noop}이 있을떄는 암호화 되지 않은 패스워드를 나타내고,
없을 때에는 암호화된 패스워드를 나타낸다.
(개발시) 하나로 통일해서 사용하는것이 유지보수에 용이하다.
스프링 시큐리티 사용시 하나의 약속이다.
시큐리티 유저 디테일 서비스 (사용자 존재여부)
package com.psd.k4.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 com.psd.k4.login.LoginServiceImpl;
import com.psd.k4.login.LoginVO;
@Service
public class SecurityUserDetailsService implements UserDetailsService {
SecurityUserDetailsService(){
System.out.println("===> SecurityUserDetailsService 실행");
}
@Autowired
private LoginServiceImpl service;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
LoginVO vo = new LoginVO();
System.out.println("===> vo 확인: " + vo );
vo.setUsername(username);
LoginVO user = service.loginOK(vo);
System.out.println("user 확인: " + user );
// 아이디가 존재하는 지를 확인하여 존재하면
// new SecurityUser(user) 객체 생성하여 값을 넘긴다.
if ( user == null) {
throw new UsernameNotFoundException (username +" 사용자 없음");
} else {
System.out.println("===> new SecurityUser(user) 로 넘어간다.!! ");
return new SecurityUser(user);
}
}
}
로그인 유저의 정보를 조회하기위해 jsp파일에서 맵핑된 곳으로 이동할때 <form>
태그의 Action를 사용하면 해당 클래스(스프링부트에서 자동으로 맵핑되는 경로)로 이동되지 않기 때문에 <form>
태그의 Action 제외한 jsp 파일을 생성하여 정확한 경로로 맵핑될 수 있도록 한다.
컨트롤러가 아닌 해당 클래스에서 아이디 존재 여부를 확인하기 때문이다.
로그인 컨트롤러
package com.psd.k4.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.psd.k4.login.LoginServiceImpl;
@Controller
public class LoginController {
@Autowired
LoginServiceImpl service;
@RequestMapping("/index.do")
String index() {
return "index.html";
}
@RequestMapping("/login.do")
String loginForm() {
return "loginForm.html";
}
@RequestMapping("/loginSuccess.do")
String loginSuccess() {
return "loginSuccess.html";
}
@RequestMapping("/accessDenied.do")
String accessDenied() {
return "accessDenied.html";
}
}
로그인 동작 경로를 지정해주는 컨트롤러이다.
권한 컨트롤러
package com.psd.k4.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.psd.k4.login.LoginServiceImpl;
@Controller
public class SecurityController {
@Autowired
LoginServiceImpl service;
@RequestMapping("/member/member.do")
String member() {
return "Success.html";
}
@RequestMapping("/manager/managerList.do")
String manager() {
return "Success.html";
}
@RequestMapping("/admin/adminList.do")
String admin() {
return "Success.html";
}
@RequestMapping("/everyone/adminList.do")
String every() {
return "Success.html";
}
}
각 권한별 이동되는 페이지를 지정하는 컨트롤러이다.
해당 경로에 따라 이동되는 경로가 달라진다.
좋은 정보 감사합니다