상세 컨텐츠

본문 제목

Security 맛보기

스프링

by e7e 2022. 5. 6. 07:25

본문

시큐리티 사용하기 위한 라이브러리 추강

pom.xml

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>5.8.6</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>5.8.6</version>
		</dependency>

ojdbc8 23.2.0.0 버젼 jdk1.8에 security상에서 password를  인식하지 못하는 상황발생!~~ㅠㅠ

		<!-- ojdbc8 
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>23.2.0.0</version>
		</dependency>
		-->
		
		<!-- https://mvnrepository.com/artifact/com.oracle.ojdbc/ojdbc8 -->
		<dependency>
			<groupId>com.oracle.ojdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>19.3.0.0</version>
		</dependency>

 

시큐리티 설정파일 맹글기!(기본 개념 잘 잡깅!)

security-context.xml

	<security:http auto-config="true">
		<security:intercept-url pattern="/oho/all"
			access="permitAll" />
		<security:intercept-url pattern="/oho/member"
			access="hasRole('ROLE_MEMBER')" />
		<security:form-login />
	</security:http>

	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="roze" password="myroze"
					authorities="ROLE_MEMBER" />
				<security:user name="admin" password="myroze"
					authorities="ROLE_MEMBER,ROLE_ADMIN" />
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>

 

위 설정파일 읽어들이깅!

root-context.xml

    <import resource="security-context.xml" />

시큐리티가 동작하기 위해서 필터체인이 동작해야 함!(시큐리티 필터체인 등록)

web.xml

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

조금씩 조금씩 다가가깅

시큐리티 태그 라이브러링

<%@ taglib uri="http://www.springframework.org/security/tags"  prefix="sec" %>

csrf 넣깅 

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

<sec:csrfInput/>

AccessDeniedHandler

AuthenticationSuccessHandler

 

spring security 설명에 보통 나오는 sql문

--drop table USERS;
create table users(
    username varchar2(50) not null primary key,
    password varchar2(50) not null,
    enabled char(1) DEFAULT '1'
);

--drop TABLE authorities;
create table authorities(
    username VARCHAR2(50) not null,
    authority VARCHAR2(50) not null,
    CONSTRAINT fk_username FOREIGN key(username) REFERENCES users(username)
);
--drop index index_username_authority;
create UNIQUE index index_username_authority on authorities(username,authority);

 

위에서는 name하고 id하고 헷깔리니 아래 처럼  쪼메 수정

drop table USERS;
create table users(
    userid varchar2(50) not null primary key,
    username varchar2(50) not null,
    password varchar2(60) not null,
    enabled char(1) DEFAULT '1',
    regdate date default sysdate
);

drop TABLE authorities;
create table authorities(
    userid VARCHAR2(50) not null,
    authority VARCHAR2(50) not null,
    CONSTRAINT fk_username FOREIGN key(userid) REFERENCES users(userid)
);
drop index index_username_authority;
create UNIQUE index index_username_authority on authorities(userid,authority);

 

권한VO

@Setter
@Getter
@ToString
public class AuthVO {
	private String userid;
	private String auth;
}

멤버VO

@Getter
@Setter
@ToString
public class MemberVO {
	private String userid;
	private String userpw;
	private String username;
	private boolean enabled;
	private Date regdate;
	
	private List<AuthVO> authList;
}

Member맵퍼

@Mapper
public interface MemberMapper {
	
	public int insertMember(MemberVO member);
	public int insertAuth(AuthVO auth);	
	public MemberVO read(String userid);
}

 

Mybatis Mapper.xml(SQL)

<?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.e7e.sec.mapper.MemberMapper">

	<resultMap type="MemberVO" id="memberMap">
		<id property="userid" column="userid"/>
		<result property="userid" column="userid"/>
		<result property="userpw" column="password"/>
		<result property="username" column="username"/>
		<result property="regdate" column="regdate"/>
		<collection property="authList" resultMap="authMap" />
	</resultMap>

	<resultMap type="AuthVO" id="authMap">
		<result property="userid" column="userid"/>
		<result property="auth" column="authority"/>
	</resultMap>
	
	<select id="read" resultMap="memberMap" parameterType="string">
				select
				mem.userid, password, username, enabled, regdate, authority
				from users mem , authorities auth 
				where mem.userid = #{userid} and mem.userid = auth.userid
	</select>
	
	<insert id="insertMember" parameterType="MemberVO">
				insert into users(userid,username,password) values(#{userid},#{username},#{userpw})
	</insert>
	
	<insert id="insertAuth" parameterType="AuthVO">
				insert into authorities (userid, authority) values(#{userid},#{auth})
	</insert>
	
</mapper>
@Slf4j
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:config/spring/root-context.xml")
public class MemberTest {
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	@Autowired
	private MemberMapper memberMapper;
	
	@Test
	@DisplayName("UserRegister")
	@Disabled
	public void insertUsers() {
	   
		MemberVO member = new MemberVO();
		
		for(int i=1; i<=10; i++) {
			member.setUserid("jinsu" + i);
			member.setUsername("niceJinsu"+i);
			member.setUserpw(passwordEncoder.encode("jinsu"+i));
			assertEquals(1,memberMapper.insertMember(member));
		}
	}

	@Test
	@DisplayName("AuthRegister")
	@Disabled
	public void insertAuth() {	   
		AuthVO auth = new AuthVO();
		
		for(int i=1; i<=10; i++) {
			auth.setUserid("jinsu" + i);
			auth.setAuth("ROLE_USER");
			assertEquals(1,memberMapper.insertAuth(auth));
		}
	}

	@Test
	@DisplayName("Select User")
	public void readUser() {
		
		MemberVO member = memberMapper.read("jinsu1");
		log.debug("check {}",member);
		member.getAuthList().forEach(authVO -> log.debug("{}",authVO));
	}
	
	
}
@Slf4j
public class CustomAccessDeniedHandler  implements AccessDeniedHandler{

	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response,
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
		  
		 //개발 중에는 필히 에러를 확인해야 함!
		log.debug("Denied Reason:" + accessDeniedException.getMessage());
		
	}

}
@Slf4j
public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler {

	@Override
	public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res,
			Authentication auth) throws IOException, ServletException {
		log.info("auth success");
		
		 Iterator<? extends GrantedAuthority> authIter= auth.getAuthorities().iterator();
		 
		 while(authIter.hasNext()) {
			 System.out.println(authIter.next());
		 }
		 
		 res.sendRedirect("/e7e/sample2/admin");
	}

}
@Slf4j
public class CustomUserDetailsService implements UserDetailsService {

	@Autowired
	private MemberMapper memberMapper;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		log.info("ckk{}"+username);
	
		MemberVO memberVO = memberMapper.read(username);
		if(memberVO != null) {
			return new CustomUser(memberVO);
		}else {
			throw new UsernameNotFoundException(username);
		}
	}

}
@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;		
	}
}

security taglibs  확인

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/security/tags"  prefix="sec"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>ADMIN</h1>
<p><sec:authentication property="principal"/></p>
<p><sec:authentication property="principal.member"/></p>
<p><sec:authentication property="principal.member.username"/></p>
<p><sec:authentication property="principal.username"   var="merong"/></p>
<p><sec:authentication property="principal.member.authList"  var="myAuths"/></p>
<h1>${merong }</h1>
<h1>${myAuths}</h1>
</body>
</html>

remember-me 체크박스를 사용하기 위해 필요한 테이블

create table persistent_logins (
    username varchar2(64) not null,
    series varchar2(64) primary key,
    token varchar2(64) not null,
    last_used timestamp not null
)
<?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-5.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
	<bean id="customUserDetailsService" class="or.pe.e7e.security.CustomUserDetailsService"/>
	<bean id="customLoginSuccessHandler" class="or.pe.e7e.security.CustomLoginSuccessHandler"/>
	<bean  id="customAccessDeniedHandler"        class="com.e7e.sec.security.CustomAccessDeniedHandler"/>
    
	<security:http >
		<security:headers>
			<security:frame-options policy="SAMEORIGIN"/>
			<security:xss-protection />
			<security:content-security-policy policy-directives="scriptsrc: 'self'" />
		</security:headers>	
		<security:access-denied-handler ref="customAccessDeniedHandler"  />
		<security:intercept-url pattern="/aaa/test" access="hasRole('ROLE_ADMIN')"/>
		<security:form-login  authentication-success-handler-ref="customLoginSuccessHandler" />
		<security:logout logout-url="/logout" invalidate-session="true" />
		<security:csrf disabled="false"/>
		<security:session-management>
			<security:concurrency-control   max-sessions="1" error-if-maximum-exceeded="true"/>
		</security:session-management>
		<security:remember-me />
	</security:http>
	
	<security:authentication-manager>
		<security:authentication-provider user-service-ref="customUserDetailsService">
			<security:password-encoder ref="bcryptPasswordEncoder"/>
<!-- 			<security:user-service>
				<security:user name="roze"  password="$2a$10$T2zgQO4yiA0hKczi60HwFOFhAldFgVgUrOJk1awAEpVgd.fn6Um/." authorities="ROLE_MEMBER"/>
			</security:user-service>
 -->		</security:authentication-provider>
	</security:authentication-manager>
</beans>

 

멀티설정 참공

<?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="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
	
	<security:http   pattern="/member/**" authentication-manager-ref="member">
		<security:headers>
			<security:frame-options policy="DENY"/>
		</security:headers>
		<security:intercept-url pattern="/member/ccc"  access="hasRole('MEMBER')" />
		<security:form-login  login-page="/member/bbb"    login-processing-url="/member/login"  />
		<security:logout logout-url="/member/logout" logout-success-url="/member/bbb"/>
	</security:http>
	
	<security:http   pattern="/merong/**"   authentication-manager-ref="merong">
		<security:headers>
			<security:frame-options policy="DENY"/>
		</security:headers>
		<security:intercept-url pattern="/merong/ccc"  access="hasRole('MEMBER')"   />
		<security:form-login  login-page="/merong/bbb"   login-processing-url="/merong/login"  />
		<security:logout logout-url="/merong/logout"  logout-success-url="/" />
	</security:http>

	<security:http  auto-config="true" pattern="/**"   authentication-manager-ref="all">
		<security:headers>
			<security:frame-options policy="DENY"/>
		</security:headers>
		<security:intercept-url pattern="/**"  access="hasRole('MEMBER')"   />
	</security:http>


	<security:authentication-manager id="member" >
		<security:authentication-provider >
			<security:password-encoder ref="passwordEncoder"/>
			<security:user-service >				
				<security:user name="aiyou"  password="$2a$10$xUmubWRg0X/BXPrgCbBADOVcqH.849Q7n.TtDIaDJaxXDVW5d1v/a" authorities="ROLE_MEMBER"/>
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
	
	<security:authentication-manager id="merong" >
		<security:authentication-provider>
			<security:password-encoder ref="passwordEncoder"/>
			<security:user-service>				
				<security:user name="jieun"  password="$2a$10$T7kAac9bON42pF4FAEgLB./QHDNHs.av1lnvnG1cr.dkVyujD3GFi" authorities="ROLE_MEMBER"/>
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
	
	<security:authentication-manager id="all" >
		<security:authentication-provider>
			<security:password-encoder ref="passwordEncoder"/>
			<security:user-service>				
				<security:user name="test"  password="$2a$10$T7kAac9bON42pF4FAEgLB./QHDNHs.av1lnvnG1cr.dkVyujD3GFi" authorities="ROLE_MEMBER"/>
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>

</beans>

 

매뉴얼 로그인 설정

@Slf4j
@Controller
public class MloginController {
	
	//@Resource(name = "authenticationManager")
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@GetMapping("/anmerong")
	public String mLogin(HttpServletRequest req) {
		
		//Autowired 잘 되었는지? 확인(D.I)
		log.debug("check:"+authenticationManager);
				
		//DB에 존재하는 아이디/암호여야 함!, 안 그럼 없는 사용자! 
		UsernamePasswordAuthenticationToken myAuth =
				new UsernamePasswordAuthenticationToken("jinsu1", "jinsu1");
		
		Authentication auth = authenticationManager.authenticate(myAuth);
		
		SecurityContext sc = SecurityContextHolder.getContext();
		sc.setAuthentication(auth);
		
		//Session이 없으면 생성
		HttpSession session = req.getSession(true);  
		//Session에 시큐리티 컨텍스트 등록
		//org.springframework.security.web.context.HttpSessionSecurityContextRepository.class에 
		// "SPRING_SECURITY_CONTEXT_KEY" 이 정의 되어 있음. 그냥 참고망
		session.setAttribute("SPRING_SECURITY_CONTEXT_KEY", sc);
				
		return "home";
	}

주의 AuthenticationManager Autowired 하기 위해서  설정파일에서  id값 주는 거 안 빼먹깅!

	<security:authentication-manager id="authenticationManager">
		<security:authentication-provider  user-service-ref="customUserDetailsService">
			<security:password-encoder ref="passwordEncoder"/>
<!-- 				<security:user-service>							
				<security:user name="test"  password="$2a$10$T7kAac9bON42pF4FAEgLB./QHDNHs.av1lnvnG1cr.dkVyujD3GFi" authorities="ROLE_MEMBER"/>
			</security:user-service>  -->
 		</security:authentication-provider>
	</security:authentication-manager>

 

 

https://www.youtube.com/watch?v=kKSzvq4Ip08 

 

'스프링' 카테고리의 다른 글

SQL로그 남기기(log4jdbc-log4j2)  (4) 2022.05.10
작은 재미 오픈API  (7) 2022.05.10
Filter(필터) 인터셉터와 구분해 주세요  (3) 2022.05.04
스케줄러(Scheduler,Quartz포함)  (0) 2022.05.04
인터셉터(가로채깅)  (1) 2022.05.03

관련글 더보기