@Controller
@RequestMapping(value = "/customer")
@Slf4j
public class CustomerController {
final String format = "CustomerController = {}";
@GetMapping(value ="/home.do")
public String homeGET() {
return "/customer/home";
}
@GetMapping(value = "/join.do")
public String joinGET() {
return "/customer/join";
}
@PostMapping(value = "/join.do")
public String joinPOST( @ModelAttribute Member member ){
log.info(format, member.toString());
return "/redirect:joinok.do"; // 주소창에 127.0.0.1:9090/ROOT/customer/joinok.do 입력 후 엔터키를 자동화
}
}
회원가입에 사용할 Controller를 작성하고
<body>
<h3>고객용 회원가입화면</h3>
<form th:action="@{/customer/join.do}" method="post">
아이디 : <input type="text" name="id" autofocus /><br />
암호 : <input type="text" name="password" value="a" /><br />
이름 : <input type="text" name="name" value="가나다" /><br />
나이 : <input type="number" name="age" value="11" /><br />
권한 변경(X)<input type="text" name="role" value="CUSTOMER" readonly/><br />
<input type="submit" value="회원가입" />
</form>
</body>
</html>
join.html을 작성하여 화면을 만든다.
@Controller
@RequestMapping(value = "/customer")
@Slf4j
@RequiredArgsConstructor
public class CustomerController {
final String format = "CustomerController = {}";
final MemberMapper memberMapper;
@GetMapping(value ="/home.do")
public String homeGET() {
return "/customer/home.do";
}
@GetMapping(value = "/join.do")
public String joinGET() {
return "/customer/join";
}
@PostMapping(value = "/join.do")
public String joinPOST( @ModelAttribute Member member ){
log.info(format, member.toString()); // 화면에 정확하게 표시되고 사용자가 입력한 항목을 member객에체 저장했음 확인
BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder(); // salt값을 자동으로 부여함. (암호화)
member.setPassword(bcpe.encode(member.getPassword())); // 기존 암호를 암호화시켜서 다시 저장함.
int ret = memberMapper.insertMemberOne(member);
if( ret == 1 ){
return "redirect:joinok.do"; // 주소창에 127.0.0.1:9090/ROOT/customer/joinok.do 입력 후 엔터키를 자동화
}
return "redirect:join.do"; // 실패시 회원가입화면으로
}
@GetMapping(value ="/joinok.do")
public String joinokGET() {
return "/customer/joinok.do";
}
}
CustomerController를 위처럼 수정하여 암호화 기능과 joinokGET을 추가한다.
<body>
<p>회원가입되었습니다.</p>
<a th:href="@{/customer/home.do}">홈으로</a>
</body>
joinok도 수정한다.
이렇게 회원가입을 하면
성공창이 뜨고
이렇게 DB에 등록된다.
@GetMapping(value ="/login.do")
public String loginGET() {
return "/customer/login";
}
CustomerController.java에 loginGET을 추가하고
<body>
<h3>고객용 로그인화면</h3>
<form th:action="@{/customer/loginaction.do}" method="post">
아이디 : <input type="text" name="id" autofocus /><br />
암호 : <input type="text" name="password" value="a" /><br />
<input type="submit" value="로그인" />
</form>
</body>
</html>
join.html을 생성한다.
@Configuration // 환경설정파일. 서버가 구동되기전에 호출됨.
@EnableWebSecurity // 시큐리티를 사용
@Slf4j
@RequiredArgsConstructor
public class SecurityConfig {
final UserDetailsService userDetailsService; // 구현한 서비스 SecurityServiceImpl.java
@Bean // 객체를 생성함. (자동으로 호출됨.)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.info("SecurityConfig => {}", "start filter chain");
// 권한 설정
http.authorizeRequests().anyRequest().permitAll();
// 로그인 처리
http.formLogin()
.loginPage("/login.do") //
.loginProcessingUrl("/loginaction.do") //action은
.usernameParameter("id")
.passwordParameter("password")
.defaultSuccessUrl("/customer/home.do") // 로그인 성공시 이동할 페이지
.permitAll();
//서비스 등록
http.userDetailsService(userDetailsService);
return http.build();
}
// 정적 자원에 스프링 시큐리티 필터 규칙을 적용하지 않도록 설정, resources/static은 시큐리티 적용받지 않음.
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
// 회원가입에서 사용했던 암호화 알고리즘 설정, 로그인에서도 같은 것을 사용해야 하니까
@Bean // 서버구동시 자동으로 실행됨 => @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityConfig를 위처럼 수정, 추가한다.
@Service
@Slf4j
@RequiredArgsConstructor
public class SecurityServiceImpl implements UserDetailsService{
final String format = "SecurityServiceImpl => {}";
final MemberMapper memberMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info(format, username);
// 아이디를 전달해서 정보를 받아옴 암호까지 받아옴
Member member = memberMapper.selectMemberOne1(username);
if( member != null ){ // 가져올 정보가 있었다. 존재하는 아이디가 있음
// Member DTO를 사용해서 처리하나 시큐리티는 User DTO를 사용함
return User.builder()
.username(member.getId())
.password(member.getPassword())
.roles(member.getRole())
.build();
}
return User.builder()
.username("_")
.password("_")
.roles("_")
.build();
}
}
service 폴더에 SecurityServiceImpl.java를 생성하여 위처럼 추가한다.
public Member selectMemberOne1(String userid);
<select id="selectMemberOne1" parameterType="com.example.dto.Member" resultType="com.example.dto.Member">
SELECT m.* FROM member m WHERE m.id = #{id}
</select>
아이디로 정보를 불러올 수 있도록 MemberMapper, memberMapper.xml에 각각 추가한다.
로그인을 해보자
로그인 정보가 일치하지 않을 때 주소창 뒤에 ?error가 붙는다.
올바른 정보로 로그인을 하면 홈화면으로 이동한다.
// 권한 설정
http.authorizeRequests()
.antMatchers("/customer/join.do").permitAll()
.antMatchers("/seller/join.do").permitAll()
.antMatchers("/admin/join.do").permitAll()
.antMatchers("/admin", "/admin/*").hasAuthority("ROLE_ADMIN") // 주소가 9090/ROOT/admin ~~ 모든 것
.antMatchers("/seller", "/seller/*").hasAnyAuthority("ROLE_ADMIN", "ROLE_SELLER")
.antMatchers("/customer", "/customer/*").hasAnyAuthority("ROLE_CUSTOMER")
.anyRequest().permitAll();
SecurityConfig의 권한 설정을 위처럼 수정한다.
antMatchers는 주소, hasAnyAuthority는 해당 주소에 들어갈 수 있는 권한이다.
CUSTOMER로 로그인 한 상태에서
/seller/home.do로 진입하니 오류가 뜨는 화면을 볼 수 있다.
// 403페이지 설정(접근권한 불가 시 표시할 화면)
http.exceptionHandling().accessDeniedPage("/403page.do");
SecurityConfig에 위 코드를 추가한다.
// 127.0.0.1:9090/ROOT/main.do
@GetMapping(value ="/403page.do")
public String pageGET(){
return "403page";
}
HomeController에 위 코드를 추가하고
<body>
<h3>접근불가</h3>
<a th:href="@{/customer/home.do}">홈으로</a>
</body>
403page.html을 생성하여 위 코드를 입력한다.
403에러 대신 위 사진처럼 뜬다.
@Bean // 객체를 생성함. (자동으로 호출됨.)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.info("SecurityConfig => {}", "start filter chain");
...
// 로그아웃 처리(GET은 안됨 반드시 POST로 호출해야 됨.)
http.logout()
.logoutUrl("/logout.do")
.logoutSuccessUrl("/home.do")
.invalidateHttpSession(true)
.clearAuthentication(true)
.permitAll();
...
}
SecurityConfig에 로그아웃을 추가한다.
<body style="padding:10px">
<h3>홈 화면</h3>
<hr />
<div th:if="${user == null}">
<a th:href="@{/customer/join.do}">고객 회원가입</a>
<a th:href="@{/login.do}">고객 로그인</a>
</div>
<div th:if="${user != null}">
<p th:text="${user.username}"></p>
<p th:text="${user.authorities}"></p>
<a th:href="@{/customer/home.do}">고객 홈(CUSTOMER)</a>
<form th:action="@{/logout.do}" method="post">
<input type="submit" value="로그아웃" />
</form>
</div>
<hr />
<div th:if="${user == null}">
<a th:href="@{/seller/join.do}">판매자 회원가입</a>
<a th:href="@{/login.do}">판매자 로그인</a>
</div>
<div th:if="${user != null}">
<p th:text="${user.username}"></p>
<p th:text="${user.authorities}"></p>
<a th:href="@{/seller/home.do}">판매자 홈(ADMIN, SELLER)</a>
<form th:action="@{/logout.do}" method="post">
<input type="submit" value="로그아웃" />
</form>
</div>
<hr />
<div th:if="${user == null}">
<a th:href="@{/admin/join.do}">관리자 회원가입</a>
<a th:href="@{/login.do}">관리자 로그인</a>
</div>
<div th:if="${user != null}">
<p th:text="${user.username}"></p>
<p th:text="${user.authorities}"></p>
<a th:href="@{/admin/home.do}">관리자 홈(ADMIN)</a>
<form th:action="@{/logout.do}" method="post">
<input type="submit" value="로그아웃" />
</form>
</div>
</body>
templates 폴더에 home.html을 만든다.
로그인하기 전
로그인했을 때
판매자 계정으로 고객, 관리자 홈으로 들어갈 때
판매자 홈으로 들어갔을 때
@Slf4j
public class LoginSuccessHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
log.info("LoginSuccessHandler => {}", authentication.toString());
String role = authentication.getAuthorities().toArray()[0].toString();
log.info("LoginSuccessHandler => {}", role);
if(role.equals("ROLE_CUSTOMER")) { // 권한이 고객
response.sendRedirect(request.getContextPath() + "/customer/home.do");
}
else if(role.equals("ROLE_SELLER")){ // 권한이 판매자
response.sendRedirect(request.getContextPath() + "/seller/home.do");
}
else if(role.equals("ROLE_ADMIN")) { // 권한이 관리자
response.sendRedirect(request.getContextPath() + "/admin/home.do");
}
else { // 위의 권한이 아닌 경우
response.sendRedirect(request.getContextPath() + "/home.do");
}
}
}
example 아래에 handler 폴더를 생성하여 그 안에 LoginSuccessHandler.java를 생성한다.
// 로그인 처리
http.formLogin()
.loginPage("/login.do") //
.loginProcessingUrl("/loginaction.do") //action은
.usernameParameter("id")
.passwordParameter("password")
.successHandler(new LoginSuccessHandler())
// .defaultSuccessUrl("/home.do") // 로그인 성공시 이동할 페이지
.permitAll();
SecurityConfig를 위처럼 수정한다.