보라코딩

스프링 시큐리티 커스텀 UserDetailsService 활용 본문

코딩/Spring

스프링 시큐리티 커스텀 UserDetailsService 활용

new 보라 2023. 5. 17. 15:50
MemberVO.java

package com.mystudy.domain;

import java.util.Date;
import java.util.List;

import lombok.Data;

@Data
public class MemberVO {

private String userid;
private String userpw;
private String userName;
private boolean enabled;

private Date regDate;
private Date updateDate;
private List<AuthVO> authList;

}

 

 

 

 

AuthVO.java

package com.mystudy.domain;

import lombok.Data;

@Data
public class AuthVO {

private String userid;
private String auth;
}

 

 

 

MemberMapper.java  (인터페이스)

 

package com.mystudy.mapper;

import com.mystudy.domain.MemberVO;

public interface MemberMapper {

public MemberVO read(String userid);
}

 

 

 

 

 

 

MemberMapper.xml
join했고
resultMap 속성에 미리 만들어둔 memberMap을 사용하면
1:N 처리 가능하다!

 

<?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.mystudy.mapper.MemberMapper">
  
  
  <resultMap type="com.mystudy.domain.MemberVO" id ="memberMap">
   <id property="userid" column="userid" />
   <result property="userid" column="userid" />
   <result property="userpw" column="userpw" />
   <result property="useranme" column="useranme" />
   <result property="regdate" column="regdate" />
   <result property="updateDate" column="updatedate" />
  <collection property="authList" resultMap="authMap">
  </collection>
  </resultMap>
  
  <resultMap type="com.mystudy.domain.AuthVO" id="authMap">
   <result property="userid" column="userid" />
   <result property="auth" column="auth" />
  </resultMap>
  
  <select id="read" resultMap="memberMap">
   select mem.userid, userpw, username, enabled, regdate, updatedate, auth
   from
   tbl_member mem left outer join tbl_member_auth auth
   on mem.userid = auth.userid
   where mem.userid = #{userid}
  </select>
  
 </mapper>

 

 

 

 

MemberMapperTests.java

 

package com.mystudy.mapper;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mystudy.domain.MemberVO;

import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class MemberMapperTests {

@Autowired
private MemberMapper mapper;

@Test
public void testRead() {

MemberVO vo = mapper.read("admin90");
log.info(vo);

vo.getAuthList().forEach(authVO -> log.info(authVO));
}
}

 

 

 

 

CustomUserDetailsService 구성 (스프링 시큐리티 구현)

 

 

 

package com.mystudy.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.mystudy.domain.MemberVO;
import com.mystudy.mapper.MemberMapper;
import com.mystudy.security.domain.CustomUser;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Log4j
public class CustomUserDetailsService implements UserDetailsService{

@Setter(onMethod_ = { @Autowired })
private MemberMapper memberMapper;


@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

log.warn("Load User By UserName : " + userName);

// userName means userid
MemberVO vo = memberMapper.read(userName);

log.warn("queried by member mapper: " + vo);

return vo == null ? null : new CustomUser(vo);


}

 

 

 

 

 

security-context.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="customAccessDenied"
class="com.mystudy.security.CustomAccessDeniedHandler"></bean>

<bean id="customLoginSuccess"
class="com.mystudy.security.CustomLoginSuccessHandler"></bean>



<bean id="bcryptPasswordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

<bean id="customUserDetailsService"
class="com.mystudy.security.CustomUserDetailsService" />


<security:http>

<security:intercept-url pattern="/sample/all"
access="permitAll" />

<security:intercept-url
pattern="/sample/member" access="hasRole('ROLE_MEMBER')" />

<security:intercept-url pattern="/sample/admin"
access="hasRole('ROLE_ADMIN')" />

<security:access-denied-handler
ref="customAccessDenied" />

<security:form-login login-page="/customLogin"
authentication-success-handler-ref="customLoginSuccess" />

<security:logout logout-url="/customLogout"
invalidate-session="true" />

</security:http>





<security:authentication-manager>
<security:authentication-provider 
user-service-ref="customUserDetailsService" >

 <security:password-encoder ref="bcryptPasswordEncoder" />
 
</security:authentication-provider>
</security:authentication-manager>

</beans>











 

CustomerUser.java   (security 패키지 안에 domain 만들어서)

 

package com.mystudy.security.domain;

import java.util.Collection;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import com.mystudy.domain.MemberVO;

import lombok.Getter;

@Getter
public class CustomUser extends User{

private static final long serialVersionUID = 1L;

private MemberVO member;

public CustomUser(String username, String password, 
Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}

public CustomUser(MemberVO vo) {

super(vo.getUserid(), vo.getUserpw(), vo.getAuthList().stream()
.map(auth -> new SimpleGrantedAuthority(auth.getAuth()))
.collect(Collectors.toList()));

this.member = vo;
}
}

 

 

 

 

 

 

 

admin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>/sample/admin page</h1>


<p>principal : <sec:authentication property="principal"/></p>
<p>사용자아이디 : <sec:authentication property="principal.username"/></p>
<p>사용자 권한 리스트  : <sec:authentication property="principal.member.authList"/></p>
<p>사용자이름 : <sec:authentication property="principal.member.userName"/></p>
<p>MemberVO : <sec:authentication property="principal.member"/></p>


<a href="/customLogout">Logout</a>


</body>
</html>

 

 

 

 

 

 

admin 계정으로 로그인 했을 때 admin.jsp 화면

 

 

 

 

 

 

 

 

 

all.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>    
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>    
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- all or member or admin -->
<h1>/sample/all page</h1>

<!-- 익명의 사용자의 경우(로그인 안한경우도)  -->
<sec:authorize access="isAnonymous()">

  <a href="/customLogin">로그인</a>

</sec:authorize>


<!-- 인증된 사용자의 경우 -->
<sec:authorize access="isAuthenticated()">

  <a href="/customLogout">로그아웃</a>

</sec:authorize>

</body>
</html>

 

 

 

 

 

 

같은 주소로 들어가도 (/sample/all)
로그인된 사람과 로그인 안된사람의 화면이 다름