-
스프링부트 스프링시큐리티 연동하기(2) UserDetailsService/AuthenticationProviderSpring Security 2019. 6. 21. 22:46반응형
DB에서 유저 정보를 직접 가져오는 인터페이스를 스프링 시큐리티에서 제공하는 UserDetailsService 인터페이스로 구현 하겠다.
1. UserDetailsService 상속 받은 SecurityService.java 인터페이스를 작성한다.
(SecurityService.java)
12345678910111213141516171819202122232425package com.devmk.test.security.service;import java.util.Collection;import com.devmk.test.vo.Member;public interface SecurityService extends UserDetailsService {// 시큐리티 사용자 인증UserDetails loadUserByUsername(String id);// 중복아이디 체크Member getSelectMeberInfo(String id) throws Exception;//회원가입int setInsertMember(Member member)throws Exception;// 비밀번호 틀린 횟수 증가int setUpdatePasswordLockCnt(String id) throws Exception;// 비밀번호 틀린 횟수 초기화int setUpdatePasswordLockCntReset(String id) throws Exception;}2. SecurityService 인터페이스 상속받은 UserServiceImpl 클래스를 작성한다.
(UserServiceImpl.java)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657package com.devmk.test.security.service;import java.util.ArrayList;import java.util.Collection;import java.util.List;import org.springframework.stereotype.Service;import com.devmk.test.vo.Member;@Servicepublic class UserServiceImpl implements SecurityService {@AutowiredLoginMapper loingMapper;@Overridepublic UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {Member member = loingMapper.getSelectMeberInfo(id);List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();if(member != null) {authorities.add(new SimpleGrantedAuthority(member.getUserRole()));member.setAuthorities(authorities);}return member;}@Overridepublic int setInsertMember(Member member) throws Exception{return loingMapper.setInsertMember(member);}@Overridepublic Member getSelectMeberInfo(String id) throws Exception{return loingMapper.getSelectMeberInfo(id);}@Overridepublic int setUpdatePasswordLockCnt(String id) throws Exception{return loingMapper.setUpdatePasswordLockCnt(id);}@Overridepublic int setUpdatePasswordLockCntReset(String id) throws Exception{return loingMapper.setUpdatePasswordLockCntReset(id);}}위의 소스에서 가장 중요한 메소드가 있다. loadUserByUsername 메소드를 반드시 구현해야 한다.
이 메소드는 UserDetailsService에 정의된 메소드로 실제 Spring security에서 User 정보를 읽을 때 사용된다.
User를 읽어왔으면 권한을 부여한다. 회원가입할때 권한을 스트링으로 저장해야하며, 권한은 MyBatis를 통해 String을 가져왔으므로
GrantedAuthority 인터페이스에 맞게 SimpleGrantedAuthority로 변환해서 Member.java vo에 있는 authorities에 setter를 한다.
그러면 로그인한 유저에게 권한이 부여된다.
3. 커스텀 로그인을 위한 AuthenticationProvider 구현
간단하게 스프링시큐리티를 구현할때 위에 작성한 서비스의 인증 결과로 로그인을 할 수 있지만
로그인 성공/실패 유무에 따라 수행하는 로직을 만들기 위해서 AuthenticationProvider 인터페이스는 구현하려고 한다.
보통 스프링 시큐리티로 커스텀 로그인을 작성할때 사용 하는 인터페이스다.
(AuthProvider.java)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172import java.util.ArrayList;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.stereotype.Component;import com.devmk.test.vo.Member;import lombok.extern.slf4j.Slf4j;@Componentpublic class AuthProvider implements AuthenticationProvider{private static final Logger logger = LoggerFactory.getLogger(AuthSuccessHandler.class);@AutowiredSecurityService securityService;//로그인 버튼을 누를 경우//첫번째 실행@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String id = authentication.getName();String password = authentication.getCredentials().toString();return authenticate(id, password);}//두번쨰 실행private Authentication authenticate(String id, String password) throws AuthenticationException{List<GrantedAuthority> grantedAuthorityList = new ArrayList<GrantedAuthority>();Member member = new Member();member = (Member)securityService.loadUserByUsername(id);if ( member == null ){logger.info("사용자 정보가 없습니다.");throw new UsernameNotFoundException(id);}else if(member != null && !member.getPassword().equals(password) ) {logger.info("비밀번호가 틀렸습니다.");throw new BadCredentialsException(id);}grantedAuthorityList.add(new SimpleGrantedAuthority(member.getUserRole()));return new MyAuthentication(id, password, grantedAuthorityList, member);}@Overridepublic boolean supports(Class<?> authentication) {return authentication.equals(UsernamePasswordAuthenticationToken.class);}}추후 작성하겠지만 WebSecurityConfigurerAdapter 를 상속받은 스프링 시큐리를 설정하는 SecurityConfig.java 내용안에
.jsp에서 아이디,비밀번호의 input name을 설정 해 줄 수 있다. 그것이 authenticate 생성자가 받는
id, password 변수명이다.
로그인이 실패하면 AuthenticationFailureHandler 상속받아 로그인 실패 로직을 구현 한다.
(AuthFailureHandler.java)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768import java.io.IOException;import javax.servlet.ServletException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.stereotype.Component;import com.devmk.test.vo.LoginLog;import com.fasterxml.jackson.databind.ObjectMapper;/*** 로그인 실패 핸들러*/@Componentpublic class AuthFailureHandler implements AuthenticationFailureHandler {@AutowiredSecurityService securityService;private static final Logger logger = LoggerFactory.getLogger(AuthFailureHandler.class);public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {String ip = CommonUtil.getClientIp(request);logger.info(" :::::::::::::::::::::::::::: 로그인실패 :::::::::::::::::::::::: ");LoginLog loginLog = new LoginLog();String id = "";String msg = "";try {id = exception.getMessage();if(securityService.getSelectMeberInfo(id) != null) {securityService.setUpdatePasswordLockCnt(id);loginLog.setLoginIp(ip);loginLog.setStatus("FAILD");securityService.setInsertLoginLog(loginLog);msg="비밀번호가 틀렸습니다.";}else {msg="아이디가 없습니다.";}} catch (Exception e) {e.printStackTrace();}response.sendRedirect("/login?msg="+msg);}}http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5; text-decoration:none">Colored by Color Scripter보통 로그, 비밀번호가 틀렸거나 아이디가 없을때의 로직을 작성할 수 있다.
(AuthSuccessHandler.java)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162import java.io.IOException;import javax.servlet.ServletException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;import org.springframework.stereotype.Component;import com.devmk.test.vo.LoginLog;/** 스프링 시큐리티를 사용하며, 로그인 성공시 부가 작업을 하려면,*별도로 authenticationSuccessHandler를 지정하지 않으면 기본적으로*/@Componentpublic class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{private static final Logger logger = LoggerFactory.getLogger(AuthSuccessHandler.class);@AutowiredSecurityService securityService;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException{String ip = CommonUtil.getClientIp(request);logger.info("::::::::::::::::::::::::::::: 로그인 성공 ::::::::::::::::::::::::::::: ");LoginLog loginLog = new LoginLog();String id = "";try {id = authentication.getName().toString();securityService.setUpdatePasswordLockCntReset(id);loginLog.setLoginIp(ip);loginLog.setStatus("SUCCESS");securityService.setInsertLoginLog(loginLog);} catch (Exception e) {e.printStackTrace();}super.setDefaultTargetUrl("/home");super.onAuthenticationSuccess(request, response, authentication);}}로그인이 성공하면 SimpleUrlAuthenticationSuccessHandler을 상속받아 성공 로직을 구현 한다.
보통 로그인 로그를 남기는 로직을 작성한다.
loadUserByUsername 에서 리턴받은 member vo가 null이면 UsernameNotFoundException타고
AuthenticationFailureHandler의 구현체가 실행되고
사용자 정보가 있으면 인증 후 객체를 리턴한다.
인증 후 객체를 생성하는 MyAuthentication클래스는
스프링 시큐리티에서 제공하는 UsernamePasswordAuthenticationToken 인터페이스를
상속받은 클래스이다. 인증이 완료된 인증 후 객체를 리턴 후
SimpleUrlAuthenticationSuccessHandler의 구현체가 실행한다.
(MyAuthentication.java)
123456789101112131415161718192021222324import java.util.List;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import com.devmk.test.vo.Member;import lombok.Data;//현재 로그인한 사용자 객체 저장 DTO@Datapublic class MyAuthentication extends UsernamePasswordAuthenticationToken{private static final long serialVersionUID = 1L;Member member;public MyAuthentication(String id, String password, List<GrantedAuthority> grantedAuthorityList, Member member) {super(id, password, grantedAuthorityList);this.member = member;}}다음 포스팅에는 WebSecurityConfigurerAdapter 를 상속받은 스프링 시큐리를 설정하는 SecurityConfig.java을 작성 과
로그인 컨틀롤러 LoginController를 작성하겠다.
반응형'Spring Security' 카테고리의 다른 글
스프링 시큐리티 자동로그인 Remember-me 안될 때 해결방법 (0) 2023.11.05 스프링 시큐리티 중복로그인 안될 때 해결방법 (0) 2023.11.03 스프링부트 스프링시큐리티 연동하기(4) 커스텀login.jsp/join.jsp/home.jsp (0) 2019.06.29 스프링부트 스프링시큐리티 연동하기(3) WebSecurityConfigurerAdapter/LoginController (0) 2019.06.23 스프링부트 스프링시큐리티 연동하기(1) Gradle/Mybatis/Oracle (0) 2019.06.15