여기저기 찾아보고, 공식홈페이지도 찾아보고…
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
0 글이 마음에 드셨다면 하트 꾸욱~