반응형
여승철
INTP 개발자
여승철
  • 분류 전체보기 (376)
    • CS (16)
      • 면접 준비 (7)
      • 운영체제 (0)
      • 네트워크 (2)
      • HTTP (6)
      • 스프링(Spring) IoC 컨테이너 (0)
      • 알고리즘 (1)
    • Web (13)
    • AWS (6)
    • Java (43)
    • JSP & Servlet (65)
      • 개념 (42)
      • 실습 (23)
    • Spring Framework (33)
    • Spring Boot (10)
    • Spring Data (22)
      • JPA (14)
      • Query DSL (7)
      • Redis (1)
    • Spring Security (9)
    • Spring Batch (4)
    • MyBatis (10)
    • Front-End (51)
      • JS (27)
      • Vue.js (17)
      • React (5)
      • JQuery (0)
      • d3.js (2)
    • DBMS (24)
      • SQL, RDBMS (16)
      • MongoDB (5)
      • Redis (3)
    • Kafka (3)
    • 리눅스 (Linux) (4)
    • 디자인 패턴 (3)
    • VCS (8)
    • API (0)
    • TOOL (3)
    • Reading Book (28)
      • 이펙티브 자바 (11)
      • Clean Code (10)
      • 1분 설명력 (4)
      • HOW TO 맥킨지 문제해결의 기술 (3)
    • C# (4)
    • NSIS (6)
    • ETC (11)

블로그 메뉴

  • 홈
  • 태그

인기 글

태그

  • querydsl
  • 스트림
  • controller
  • 이펙티브 자바
  • EC2
  • 게시판
  • servlet
  • HTTP
  • mybatis
  • Dao
  • JSTL
  • 환경 세팅
  • 맥킨지
  • 로그인
  • JDBC
  • jsp
  • Spring Batch
  • ubuntu
  • 디자인 패턴
  • 회원 관리

최근 댓글

최근 글

hELLO· Designed By 정상우.
여승철

INTP 개발자

스프링 시큐리티를 이용한 로그인
ETC

스프링 시큐리티를 이용한 로그인

2022. 9. 30. 03:40
반응형

실행 결과

1) boardList에서 글작성 클릭 시 로그인 창 출력
   로그인을 하지 않아도 글을 보거나 게시판 목록 접근 가능하지만 글 등록은 로그인하여야 함

 

 

 

2) ROLE_ADMIN 권한을 갖고 있는 아이디 비밀번호로 접속

3) 게시글 작성

 

4) 해당 게시글 확인

 

 

5. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

테이블 설계


▶member 테이블

create table member(
userid varchar(50) primary key,
userpw varchar(100) not null,
username varchar(100) not null,
regdate datetime not null default current_timestamp,
updatedate datetime,
enabled char(1) default '1'
);

 

▶member_auth 테이블

create table member_auth(
userid varchar(50) not null,
auth varchar(50) not null,
foreign key(userid) references member(userid)
);

 

 

 

 

 

 

 

 

 

 

회원 도메인, 회원 Mapper 설계


회원 도메인과 회원 Mapper 부터 설계해준다.

 

▶Member

public class Member {
	private String userid;
	private String userpw;
	private String userName;
	private boolean enabled;

	private String regDate;
	private String updateDate;
	private List<Auth> authList;
	
	
	public String getUserid() {
		return userid;
	}
	public void setUserid(String userid) {
		this.userid = userid;
	}
	public String getUserpw() {
		return userpw;
	}
	public void setUserpw(String userpw) {
		this.userpw = userpw;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public boolean isEnabled() {
		return enabled;
	}
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	public String getRegDate() {
		return regDate;
	}
	public void setRegDate(String regDate) {
		this.regDate = regDate;
	}
	public String getUpdateDate() {
		return updateDate;
	}
	public void setUpdateDate(String updateDate) {
		this.updateDate = updateDate;
	}
	public List<Auth> getAuthList() {
		return authList;
	}
	public void setAuthList(List<Auth> authList) {
		this.authList = authList;
	}
}

여러 개의 사용자 권한을 가질 수 있도록 authList

 

 

 

▶Auth

public class Auth {

	private String userid;
	private String auth;

	public String getUserid() {
		return userid;
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public String getAuth() {
		return auth;
	}

	public void setAuth(String auth) {
		this.auth = auth;
	}

}

 

 

 

 

▶member.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.spring.boardapp.dao.MemberDao">


  <resultMap type="com.spring.boardapp.domain.Member" id="memberMap">
    <id property="userid" column="userid"/>
    <result property="userid" column="userid"/>
    <result property="userpw" column="userpw"/>
    <result property="userName" column="username"/>
    <result property="regDate" column="regdate"/>
    <result property="updateDate" column="updatedate"/>
    <collection property="authList" resultMap="authMap">
    </collection> 
  </resultMap>
  
  <resultMap type="com.spring.boardapp.domain.Auth" id="authMap">
    <result property="userid" column="userid"/>
    <result property="auth" column="auth"/>
  </resultMap>
  
  <select id="getAuth" resultMap="memberMap">
	SELECT mem.userid,  userpw, username, enabled, regdate, updatedate, auth
	FROM member mem 
	LEFT OUTER JOIN member_auth auth 
	ON mem.userid = auth.userid 
	WHERE mem.userid = #{userid} 
  </select>

</mapper>

Member가 Auth를 갖고 있는 1:N 관계이므로 이럴 때 resultMap을 사용하는 것이 좋다.

 

 

 

▶MemberDaoImpl.java   

MemeberDao 인터페이스는 생략하도록 하겠다.
@Repository("memberDao")
public class MemberDaoImpl implements MemberDao {
	
	@Autowired
	SqlSession sqlSession;
	
	@Override
	public Member get(String userid) {
		// TODO Auto-generated method stub
		return sqlSession.selectOne("getAuth", userid);
	}
}

 

 

 

 

 

 

 

 

 

 

 

BCryptPasswordEncoder 이용


스프링 시큐리티에서 제공되는 BCryptPasswordEncoder 클래스를 이용해서 패스워드를 암호화해서 처리되도록 하겠다. 참고로 스프링 시큐리티 5부터는 패스워드 암호화를 반드시 처리해주어야 한다.

 

BcryptPasswordEncoder는 스프링 시큐리티의 API안에 포함되어 있다.

때문에 그냥 빈을 추가해주고 PasswordEncoder를 bcryptPasswordEncoder로 지정해준다.

	
    ...
	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
	
    <security:http>
		...	
	</security:http>
	
	
	
	<security:authentication-manager>
		<security:authentication-provider>
		
			<security:jdbc-user-service data-source-ref="dataSource"/>
            
			<security:password-encoder ref="bcryptPasswordEncoder" />
      
		</security:authentication-provider>
	</security:authentication-manager>
	
</beans>

 

 

 

 

 

 

 

 

커스텀 UserDetailsService 활용


CustomUserDetailsService 구성

  • 스프링 시큐리티의 UserDetailsService를 구현
  • MemberDao 타입의 인스턴스를 주입받아서 실제 기능을 구현

 

 

UserDetailsService 구성

  • 추상 메소드: loadUserByUsername()
  • 반환 타입: UserDetails 타입

 

반환 타입은 UserDetails 타입이기 때문에 우리가 적용하려는 Member 타입을 아래와 같은 구현을 통해 UserDetails 타입으로 반환해주어야 한다.

 

 

 

▶CustomUser.java

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.spring.boardapp.domain.Member;

public class CustomUser extends User {
	
	private static final long serialVersionUID = 1L;
	
	private Member member;
	
	public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
		super(username, password, authorities);
	}

	public CustomUser(Member member) {

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

		this.member = member;
	}
}
  • User 클래스를 상속하기 때문에 부모 클래스의 생성자를 호출해야 정상적인 객체를 생성할 수 있다.
  • Member를 파라미터로 전달하여 User 클래스에 맞게 생성자를 호출
  • Auth 인스턴스는 GrantedAuthority 객체로 변환해야 하므로 stream()과 map()을 이용하여 처리

 

 

 

 

▶CustomUserDetailsService.java

public class CustomUserDetailsService implements UserDetailsService {

	@Resource(name="memberDao")
	private MemberDao memberDao;

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

		System.out.println("Load User By UserName : " + userName);

		// userName means userid
		Member member = memberDao.get(userName);

		System.out.println("queried by member mapper: " + member);

		return member == null ? null : new CustomUser(member);
	} 

}

 

 

▶security-context.xml

	...
    
	<bean id="customAccessDenied" class="com.spring.boardapp.security.CustomAccessDeniedHandler"></bean>
	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
	<bean id="customUserDetailsService" class="com.spring.boardapp.security.CustomUserDetailsService"></bean>
	
    
    
    <security:http>
		...
	</security:http>
	
	
	
	<security:authentication-manager>
		<security:authentication-provider user-service-ref="customUserDetailsService">
		
			<security:password-encoder ref="bcryptPasswordEncoder" />
			
		</security:authentication-provider>
	</security:authentication-manager>
	
</beans>

빈 설정하는 부분에 대해 궁금한 것이 있는 사람은 다음 링크를 보고 오도록 하자 [ 간단 로그인 구현 ]

 

 

 

 

 

 

로그인에 따른 권한 변화


 

▶BoardController.java

	@RequestMapping(value = "/regist")
	@PreAuthorize("isAuthenticated()") //로그인한 사용자 모두 등록 가능
	public String registBoard(@RequestParam Map<String, Object> paramMap, Model model,

 

 

 

▶boardList.jsp

		<tr>
			<td>
				<sec:authorize access="isAnonymous()">
					<input type="button" value="로그인" onclick="location.href='/customLogin'">
				</sec:authorize>
				<sec:authorize access="isAuthenticated()">
					<form role="form" method="post" action="/customLogout">
						<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
						<input type="submit" value="로그아웃" />
					</form>
				</sec:authorize>
			</td>
			<td align="right"><input type="button" value="글 작성" onclick="location.href='/board/regist'">
		</tr>

 

위와 같은 형식으로 적용하고자 하는 것에 적용

 

 

https://yeo-computerclass.tistory.com/351

 

[spring security] @PreAuthorize, @PostAuthorize 어노테이션 이용한 스프링 시큐리티 설정

어노테이션을 이용하여 spring security를 설정하기 위해선 설정을 해주어야 한다. 설정 XML 설정 기반인 경우 ▶servlet-context.xml Java 설정 기반인 경우 ▶ServletConfig.java @EnableWebMvc @ComponentScan(..

yeo-computerclass.tistory.com

 

반응형

'ETC' 카테고리의 다른 글

[springMVC + Ajax] 게시판 첨부파일 추가  (0) 2022.09.27
Spring + Ajax 파일 다운로드 시 Internet Explorer, Edge에서 한글이름 깨짐  (0) 2022.09.26
Spring + Ajax 파일 다운로드  (0) 2022.09.26
Spring + Ajax 섬네일 파일 생성, 이미지 파일인지 구분, 이미지 클릭 시 원본 이미지 출력  (0) 2022.09.25
Spring + Ajax 중복된 파일 이름 해결  (0) 2022.09.25
    여승철
    여승철

    티스토리툴바