@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//아래의 mavMAtchers에 따라 승인
http.authorizeRequests()
// 해당 서비스는 모두 사용 가능 (로그인 필요 X)
.mvcMatchers("/", "/hello","/all/**", "/join").permitAll()
// 해당 서비스는 admin만 사용 가능
.mvcMatchers("/admin/**").hasRole("admin")
// 해당 서비스는 로그인 하면 사용 가능
.anyRequest().authenticated();
//로그인
//로그인 폼 명 /login
http.formLogin().loginPage("/login")
.permitAll()
.defaultSuccessUrl("/service1");
//로그아웃
//invalidateHttpSession = 로그 아웃 후 세션 파기하는 속성
http.logout().logoutRequestMatcher(
new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true);
http.httpBasic();
}
}
controller
@Controller
@Setter
public class MemberController {
@Autowired
private MemberDAO dao;
@Autowired
private PasswordEncoder passwordEncoder;
//GET 만 정의하면 됌.
//POST는 Spring Security가 알아서 해주기때문에
//POST는 VIEW 페이지에서만 정의하면 됨
@RequestMapping(value="/login", method=RequestMethod.GET)
public void login() {
}
@GetMapping("/join")
public void joinForm() {
}
@PostMapping("/join")
public ModelAndView joinSubmit(MemberVO m) {
ModelAndView mav = new ModelAndView("redirect:/login");
//사용자가 입력한 암호를 가져와서 해당 암호를 암호화하는 작업
m.setPwd(passwordEncoder.encode(m.getPwd()));
m.setRole("user");
dao.insert(m);
return mav;
}
}
로그인 서비스 페이지(스프링 마법)
loadUserByUsername()
@Service
@Setter
public class MemberService implements UserDetailsService {
@Autowired
private MemberDAO dao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
System.out.println("사용자 로그인 처리");
System.out.println(username);
MemberVO m = dao.findById(username);
if(m == null) {
throw new UsernameNotFoundException(username);
}
UserDetails user = User.builder()
.username(username)
.password(m.getPwd())
.roles(m.getRole()).build();
return user;
}
}
Mapper
<?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="member">
<select id="findById" resultType="memberVO">
select * from member100 where id=#{id}
</select>
</mapper>
DAO / DBManaver 생략 했음.
로그인 회원 상태유지
로그인 한 회원의 상태를 유지하기 위해서는 Controller 에서 시큐리티를 통해 로그인 회원 정보를 가져오면 됌
로그인 한 회원의 id를 가져 왔으니, Model 이나 Session, Cookie에 상태 유지 하면 됌
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); User user = (User) authentication.getPrincipal(); String id = user.getUsername()
자바로 만들어진 소프트웨어는 수많은 클래스로 구성되고, 클래스와 클래스는 서로 관계에있음 has a or is a 관계이다.
두 클래스의 관계가 '클래스 vs 클래스'이면서 new에 의해 객체를 생성하는 건 '강한 의존관계에 있다' 라고 표현
'강한 의존관계'를 '약간 의존관계'로 만드는게 DI의 개념이다.(의존관계에 대한 객체 주입을 Spring이 알아서 해줌) 어떤식으로 Spring이 해주냐면 New로 객체를 생성하는 것이 아닌 별도의 환경설정 파일에서 객체를 제공해준다
스프링 초기에는 XML로 환경설정을 하였지만 XML이 너무 비대해져서 그래서 어노테이션 기반으로 객체를 생성하는 개념이 나왔으나 이것 또한 불편해서 자동으로 의존관계 설정하는 개념이 등장
발전 변천사
XML로 객체 생성 -> 어노테이션 기반 생성 -> 현재는 오토스캔하여 완전 자동 생성으로 발전
Spring에서 AOP (Aspect Oriented Programming) 란?
여러 곳에서 사용되는 Method를 한 곳에서 Spring이 유지보수 가능하도록 도와줌
AOP는 메소드에 대한 이야기 (공통 관심사항'과 '핵심 관심사항'의 Bind를 스프링이 해줌)
공통 관심사항, 핵심 관심사항 개념을 알아야함 - 공통 관심사항(cross-cutting concern) - 공통으로 처리되는 사항 (포괄적 개념) - 핵심 관심사항(core concern) - 핵심으로 처리되는 사항
대표적인 사용 용도 1. 패키지 내 메소드 Logging 2. 패키지 내 메소드 로직 수행 시간 확인
주요 용어 설명
Aspect - 여러 곳에서 사용되는 '공통 관심 사항' 들을(cross-cutting concern)를 모듈화 - @Aspect Target - Aspect 가 적용되는 곳 - 어떤 대상에 부가 기능을 부여할 것인가
Advice - Aspect 에서 실질적인 기능에 대한 구현체 - 어떤 부가 가능 인지 (Before, AfterReturning, AfterThrowing, After, Around)
Joint point - Advice 가 Target 에 적용되는 시점메서드 진입할 때, 생성자 호출할 때, 필드에서 값을 꺼낼 때 등등 - 어디에 적용할 것인가? 메서드, 필드, 객체, 생성자 등 Pointcut - Joint point 의 상세 스펙을 정의한 것 - 실제 Advice가 적용될 지점, Spring AOP에서는 advice가 적용될 메서드를 선정 - @Pointcut
Aspect
여러 곳에서 사용되는 '공통 관심 사항' 들을(cross-cutting concern)를 모듈화
Advice
'공통 관심 사항'을 위해 만들어 놓은 클래스
before - 핵심 관심사항처리 전 '공통 관심사항' 동작 - @Before
After Returning - 핵심 관심사항이성공적으로 마무리 되었을 경우 '공통 관심사항' 동작 - @AfterReturning - 대상 객체가 리턴한 값을 사용하고자 할 때는 returning="ret" 에 담아서 사용
After Throwing - '핵심 관심사항'실패했을 때 '공통 관심사항' 동작 (예외) - @AfterThrowing - 예외에 접근하기 위해 throwing="ex" 에 담아서 사용
After - 핵심 관심사항성공여부 관계 없이 무조건 공통 관심사항 동작
Around - 핵심 관심사항이처리되기 전 후 or 예외 발생 시 공통 관심사항 동작 -JProceedingJoinPoint 타입의 joinPoint를 반드시 매개변수로 가져야함
JoinPoint
'공통 관심 사항'이 호출되는 지점
필요한 메소드가 호출되는 부분을 jointPoint 라고 부름
대상 객체 및 호출되는 메서드의 정보를 담고 있음.
joinpoint.getSignature()
호출되는 메서드의 정보를 담고 있음
@After("test()")
public void afterError(JoinPoint joinPoint) {
//toShortString() 메소드 이름 반환
String methodName = joinPoint.getSignature().toShortString();
System.out.println(methodName + "메소드가 완료되었습다.");
}
웹 어플리케이션에서 사용자의 요청을 가로채어 먼저 동작하여 흐름을 제어하기 위한 용도로 사용
예시 : 어떤 흐름이냐면 어떤 서비스를 이용할 때 Login을 꼭 진행하도록 함
인증 / 인가(role)를 나눌 수 있음 (Spring Security를 수동으로 하는 느낌)
doFilter() 함수에서 흐름제어를 해야함 (LoginFilter.java)
- 현재 세션을 getSession()으로 가져와야 하는데 ServletRequest에는 getSession()이 없으므로 HttpServletRequest로 Type Casting 후 getSession() 사용하여 현재 로그인 한 id 세션 가져와 흐름제어 할 것임 - 23번째에 줄에 namespace에서실수가 많으니 주의할 것 (서비스 요청 할 때 '프로젝트 이름/서비스do' )
//admin에 있는 모든 요청
@WebFilter("/admin/*")
public class LoginFilter extends HttpFilter implements Filter {
// ...
// ...
// ...
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain
)throws IOException, ServletException {
System.out.println("필터가 동작함");
//세션에 로그인 정보를 받아와서 로그인하지 않았으면 로그인 한데로 보내고
// 로그인 했으면 조건에 부합하는 곳으로 보낸다
HttpSession session = ((HttpServletRequest)request).getSession();
String id = (String)session.getAttribute("id");
if(id!=null) {
//로그인 성공 시 doFilter 실핼
chain.doFilter(request, response);
}
else {
((HttpServletResponse)response).sendRedirect("/boardTest/login.do");
}
}
// ...
// ...
// ...
}
스프링의 주요 특징
1. DI
클래스들의 의존관계를 스프링 컨테이너가 제공함으로써 좀 더 느슨하게 설정하여 수정에 대한 요구에 유연하게 대처할 수 있음
2. AOP
핵심코드에 공통모듈을 포장하여 실행하는 방식
특정 요청에 따른 '핵심관심사항'과 '공통관심사항'이 있음
'공통관심사항'에 대하여 빈번한 수정에 대한 요구를 유연하게 대처하기 위하여 AOP를 적용하여 사용
댓글