여기저기 찾아보고, 공식홈페이지도 찾아보고…
Spring Boot Security 로 검색하면..
스프링부트에서의 Spring Security 설정법이 많이 나오지 않는다..
Spring Framework 에서 설정했던 spring-security.xml 과
같은 xml을 기반으로 한 설정 방법이 우루루…
저 방법은… 이전에 정리해둔게 있으므로 패스..
+나는 스프링부트를 기준으로 하고 싶다!!
(제대로 된 건지는 모르겠으나.. 정상 작동은 되는중이라 포스팅 시작)
첫번째로 Spring Security 를 사용하기 위한 의존성 추가(pom.xml)
프로젝트 우클릭 -> Spring -> Edit Starters 클릭
Security 검색 -> Spring Security 선택 후 OK
(어라.. Spring Data JPA 는 사용 안하므로 제거해야지..)
Lombok 의 경우 Model 또는 DTO 또는 VO 생성 시
Getter, Setter 를 만들어 주지 않아도
@Data, @Getter, @Setter 와 같은 방식으로 어노테이션 한줄로 처리해주는
고마운 라이브러리..
(설치방법은 위 처럼 추가 후 해당 jar를 실행 후 IDE 를 선택해주는 것으로 끝난다.)
(ini 파일에 -javaagent:C:\sts-4.6.1.RELEASE\lombok.jar 가 추가됨)
데이터를 조회한 후 저장할 Model 생성
Member.java
@Data public class Member { private String userId; private String userName; private String password; }
MemberMapper.java
@Mapper public interface MemberMapper { Member findByUserId(String userId); }
이전에는 @Repository 를 사용하였는데.. @Mapper 어노테이션이 있기에 사용
MemberMapper.xml
<?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="com.ep.mapper.MemberMapper"> <select id="findByUserId" parameterType="java.lang.String" resultType="com.medica.ep.model.Member"> SELECT USER_ID AS userId, USER_NAME AS userName, PASSWORD AS password FROM USER WHERE USER_ID = #{userId} </select> </mapper>
★ 여기서 부터 중요.. 삽질 많이 한 부분..
MemberService.java
(UserDetailsService 인터페이스를 implements 받아 구현한다)
(입력받은 아이디에 대한 정보를 조회하여 값을 return 해주는 클래스)
(권한 설정도 여기서 해줘야 할 것으로 보여짐)
@Service public class MemberService implements UserDetailsService { @Autowired private MemberMapper memberMapper; @Override public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { Member member = memberMapper.findByUserId(userId); List<GrantedAuthority> authorities = new ArrayList<>(); if(member != null) { authorities.add(new SimpleGrantedAuthority("USER")); }else { new UsernameNotFoundException("아이디 없음"); } //부서에 따라 권한 관리 String role = ""; String deptCd = member.getDeptCd(); if("1000".equals(deptCd)){ role = "DEPT"; } authorities.add(new SimpleGrantedAuthority(role)); //권한 설정 끝 return new User(member.getUserId(), member.getPassword(), authorities); } }
memberMapper.findByuserId(userId)
에서 조회된 결과를 기준으로 각 권한(ROLE)을 설정 해준다.
return 하는 부분에서 한참을 해매였는데…
https://spring.io/guides/gs/securing-web/
공홈에 있는 예제대로 만들어 본 다음 하나씩 바꾸어보니 되더라-_-
공홈에서는 User를 return 하는것이 아닐 ㅏUserDetails 라는 객체 자체를 return
하는 방식이나 권한이 여러개(ex : 관리자의 경우 관리자이면서 사용자) 인 경우
※ User 생성자에 있는 ID 와 Password는
memberService 를 호출하는 곳에서 로그인 시 입력한 ID 와 Password를 비교
(Spring Security 내부적으로 비교하는 것으로 보인다.. equals 니 이런게 안보임;;)
★Spring Boot Security 와 Spring Security 의 차별(?)성이 나타나는 부분..
WebSecurityConfig.java (클래스명은 자유)
Spring Security 에서는 Auth블라블라 Provider 블라블라~ 에서 권한을~~
였던 것 같은데… Spring Boot Security 의 경우 생각보다? 간단하다.
(그런거치고는 삽질을 많이 했지만…)
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MemberService memberService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //.antMatchers("/admin").hasRole("ADMIN") .antMatchers("/").permitAll() .anyRequest().authenticated() .and().csrf() .ignoringAntMatchers("/user/login", "/user/logout") .and().formLogin() .loginPage("/user/login").permitAll() .and().logout() .logoutUrl("/user/logout") .logoutSuccessUrl("/") .invalidateHttpSession(true) .permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(memberService).passwordEncoder(passwordEncoder()); } @SuppressWarnings("deprecation") @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); //return new BCryptPasswordEncoder(); //return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
.antMatchers(“/admin”).hasRole(“ADMIN”) : /admin 경우 role 이 ADMIN
.antMatchers(“/”).permitAll() : “/” 경로는 모두 접근 가능
.anyRequest().authenticated() : 그외 경로는 모두 권한이 있어야 가능
.and().csrf()
.ignoringAntMatchers(“/user/login”, “/user/logout”)
로그아웃 하는 경우 csrf 값 무시
.loginPage(“/user/login”).permitAll() : 로그인 경로 모두 접근
.logoutUrl(“/user/logout”)
.logoutSuccessUrl(“/”)
.invalidateHttpSession(true)
.permitAll();
로그아웃 페이지
로그아웃 성공시 이동 Url
세션은 만료 처리
모두 접근 가능
Spring-security 에서는 Auth블라Provider 에서 설정했던 부분이
Spring Boot 로 넘어오면서(?) 아래와 같이 바뀐게 아닌가..?
라는 추측이 든다.
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth 객체에 userDetailsService 객체를 인자로 갖는 생성자를 호출 할때
MemberService.java -> loadUserByUsername 에서 return 하였던
User의 아이디, 패스워드, 권한을 기준으로 아이디, 패스워드 검증을 하고
아이디와 패스워드가 일치하면 전달 받은 권한을 해당 세션 권한을 부여하는…
그러한 로직이지 않을까..(?)
passwordEncoder() 의 경우 기존 DB 에 암호화처리가 되어 있지 않기 때문에
NoOpPasswordEncoder를 사용하였다.
또는 MemberService.loadUserByUsername 클래스에서
return 값중 password 부분 앞에 {noop} 를 문자열로 추가하여도 된다.
큰 영향을 미치지 않는 Controller.java 와 jsp 파일들..
IndexController.java
@Controller public class IndexController { @RequestMapping("/") public String index() { return "index"; } }
UserController.java
@RequestMapping(value = "/user") @Controller public class UserController { @RequestMapping(value = "userPage") public String hello() { return "user/userPage"; } @RequestMapping(value = "login") public String login() { return "user/login"; } @RequestMapping(value = "/logout") public String logout(HttpServletRequest request, HttpServletResponse response) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { new SecurityContextLogoutHandler().logout(request, response, auth); } return "redirect:/"; } }
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="/include/common.jsp"%> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Spring Security Example</title> </head> <body> <h1>Welcome!</h1> <p>Click <a href="/user/userPage">here</a> to see a greeting.</p> </body> </html>
/user/login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="/include/common.jsp"%> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Spring Security Example </title> </head> <body> <c:if test="${!empty param.error}"> <div> Invalid username and password. </div> </c:if> <c:if test="${!empty param.logout}"> <div> You have been logged out. </div> </c:if> <form action="/user/login" method="post"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form> </body> </html>
/user/logout.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="/include/common.jsp"%> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Spring Security Example </title> </head> <body> </body> </html>
/user/userPage.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="/include/common.jsp"%> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Hello World!</title> </head> <body> <h1>Hello ${request.remoteUser}!</h1> <form action="/user/logout" method="post"> <input type="submit" value="Sign Out"/> </form> </body> </html>
/include/common.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <fmt:requestEncoding value="UTF-8"/> <% response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); %> <%@ page session="true"%>
결과화면…
here 클릭..
MemberMapper 에서 조회 해 올 수 있는 ID와 password 입력
아이디 or 패스워드 다른 경우… parameter 에 error 추가 확인
정상적으로 로그인 된 경우.. user/userPage 로 이동된다
Sign Out 클릭시
로그아웃 되면서 메인페이지로 이동 된다.
※ 참고
아이디 패스워드를 입력하기전
/user/userPage 입력시 /user/login 페이지로 이동 된다.
(당연한건가…?)
참고자료
https://spring.io/guides/gs/securing-web/
https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released
https://github.com/spring-guides/tut-spring-security-and-angular-js/issues/71
https://victorydntmd.tistory.com/328