Ex) 초기 설정
- 먼저, 관리자 페이지를 구성한다. ex)admin,backoffice, cms가 들어간다. http://admin.fastlms.co.kr
- 서브 도메인을 따로 둘 수도 있다. http://www.fastlms.co.kr/ admin
- adminService를 만든다. (member쪽을 만들었듯이)
-> AdminMainController
package com.zerobase.fastlms.admin;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminMainController {
@GetMapping("/admin/main.do")
public String main () {
return "admin/main" ;
}
}
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 확인</title >
</head >
<body >
<h1 > 관리자 메인 화면</h1 >
</body >
</html >
- Security 클래스에 permitAll()로 등록되어 있지 않아서 로그인한 다음에야 admin/main 페이지에 접속이 가능하다.
===================== ==================== 오류 ==========================================
- 오류: 로그인 안해도 바로 관리자 화면이 뜬다.
- 원인: ?
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 확인</title >
</head >
<body >
<h1 > 관리자 메인 화면</h1 >
<div >
<a href ="/admin/member/list.do" > 회원 관리</a >
|
<a href ="#" > 카테고리 관리</a >
|
<a href ="#" > 강의 관리</a >
<br >
</div >
</body >
</html >
- AdminMemberController
package com.zerobase.fastlms.admin;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminMemberController {
@GetMapping("/admin/member/list.do")
public String list () {
return "admin/member/list" ;
}
}
- list 파일 만든다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 관리자 화면</title >
</head >
<body >
<h1 > 관리자 회원 관리</h1 >
<div >
<a href ="/admin/main.do" > 관리자 메인</a >
|
<a href ="/admin/member/list.do" > 회원 관리</a >
|
<a href ="#" > 카테고리 관리</a >
|
<a href ="#" > 강의 관리</a >
|
<a href ="/member/logout" > 로그 아웃</a >
<br >
</div >
</body >
</html >
- 위 화면에서 로그아웃 누르면?
Ex) 관리자 권한과 사용자 권한을 구분하려면?
- Entity에 필드 추가 - 관리자인지 회원인지 여부를 나타낸다.
package com.zerobase.fastlms.member.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.time.LocalDateTime;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
@Entity
public class Member {
@Id
private String userId;
private String userName;
private String phone;
private String password;
private LocalDateTime regDt;
private boolean emailAuthYn;
private LocalDateTime emailAuthDt;
private String emailAuthKey;
private String resetPasswordKey;
private LocalDateTime resetPasswordLimitDt;
private boolean adminYn;
}
cf) yml 파일에 jpa 설정이 있기 때문에 엔티티에 데이터를 추가하면 DDL을 자동 생성하고 데이터베이스까지 저장한다.
- 데이터베이스에 저장된 계정 중 하나를 관리자 user(true)로 만든다.
Ex) 회원 가입 후, 이메일이 활성화 되는 것은 한 번만 가능하도록 수정
- Impl 클래스
-> 회원 가입했을 때 받은 회원 가입 메일의 링크를 들어가면 활성화 페이지가 보인다. 이는 한 번만 가능해야한다.
-> 따라서 impl클래스를 수정한다. (여러 번 활성화되지 않도록한다.)
- impl - emailAuth()메서드를 수정한다.
@Override
public boolean emailAuth (String uuid) {
Optional<Member> optionalMember = memberRepository.findByEmailAuthKey(uuid);
if (!optionalMember.isPresent()){
return false ;
}
Member member = optionalMember.get();
if (member.isEmailAuthYn()){
return false ;
}
member.setEmailAuthYn(true );
member.setEmailAuthDt(LocalDateTime.now());
memberRepository.save(member);
return true ;
}
Note) 실행 결과
- 아래처럼 실패라고 나와야 정상이다.
Ex)
- loadUserByUserName() 메서드에서 user가 관리자인 경우는?
-> "ROLE_ADMIN"을 추가한다.
-> SimpleGrantedAuthority("ROLE_USER"): GrantedAuthority 인터페이스를 구현한 클래스이다.
@Override
public UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {
Optional<Member> optionalMember = memberRepository.findById(username);
if (!optionalMember.isPresent()){
throw new UsernameNotFoundException("회원 정보가 존재하지 않습니다." );
}
Member member = optionalMember.get();
if (!member.isEmailAuthYn()){
throw new MemberNotEmailAuthException("이메일 활성화 이후에 로그인 해주세요" );
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER" ));
if (member.isAdminYn()){
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN" ));
}
return new User(member.getUserId(), member.getPassword(), grantedAuthorities);
}
- SecurityConfig에 관리자 권한 관련해서 내용을 추가한다.
-> AntPathRequestMatcher(String pattern): 대소문자를 구분하지 않고 모든 http 메서드와 일치하는 특정 패턴으로 Matcher을 만든다.
package com.zerobase.fastlms.configuration;
import com.zerobase.fastlms.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final MemberService memberService;
@Bean
PasswordEncoder getPasswordEncoder () {
return new BCryptPasswordEncoder();
}
@Bean
UserAuthenticationFailureHandler getFailureHandler () {
return new UserAuthenticationFailureHandler();
}
@Override
protected void configure (HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(
"/"
, "/member/register"
, "/member/email-auth"
, "/member/find-password"
)
.permitAll();
http.authorizeRequests()
.antMatchers("/admin/**" )
.hasAnyAuthority("ROLE_ADMIN" );
http.formLogin()
.loginPage("/member/login" )
.failureHandler(getFailureHandler())
.permitAll();
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout" ))
.logoutSuccessUrl("/" )
.invalidateHttpSession(true );
super .configure(http);
}
@Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService)
.passwordEncoder(getPasswordEncoder());
super .configure(auth);
}
}
- 관리자가 아닌 일반 계정으로 로그인
- 다음과 같이 관리자 페이지를 들어가려고 시도하면 오류가 난다. => 추가 작업을 한다.
- 관리자 계정 접속: 관리자 메인 화면 접속 가능
- 일반계정 처리 작업
-> SecutiryConfig 클래스 수정
-> logoutSuccessUrl("/"): 로그아웃을 성공하면 메인 페이지로 이동한다.
-> invalidateHttpSession(true): 로그아웃 했으니까 세션을 초기화한다.
@Override
protected void configure (HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers(
"/"
, "/member/register"
, "/member/email-auth"
, "/member/find-password"
)
.permitAll();
http.authorizeRequests()
.antMatchers("/admin/**" )
.hasAnyAuthority("ROLE_ADMIN" );
http.formLogin()
.loginPage("/member/login" )
.failureHandler(getFailureHandler())
.permitAll();
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout" ))
.logoutSuccessUrl("/" )
.invalidateHttpSession(true );
http.exceptionHandling()
.accessDeniedPage("/error/denied" );
super .configure(http);
}
- 메인 컨트롤러에 추가
@RequestMapping("/error/denied")
public String errorDenied () {
return "error/denied" ;
}
- denied 파일 추가한다.
<!doctype html >
<html lang ="ko" xmlns:th ="http://www.thymeleaf.org" >
<head >
<meta charset ="UTF-8" >
<title > 에러 페이지</title >
</head >
<body >
<h1 > 에러 페이지</h1 >
<p > 접근 권한이 없습니다.</p >
<div >
<a href ="/" > 홈으로 이동</a >
</div >
</body >
</html >
Note) 회원이 관리자 페이지에 접근하는 경우 - 실행 결과