MelonPeach

DB연동은 되어있는 상태에서 시작합니다.

 

- 시큐리티 로그인기능

- 자동로그인 기능

 

 

 

 

1. pom.xml

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>${security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<version>${security.version}</version>
		</dependency>

 

pom.xml에 추가해줍니다.

 

	<properties>
		<spring.maven.artifact.version>4.3.25.RELEASE</spring.maven.artifact.version>
		<egovframework.rte.version>3.10.0</egovframework.rte.version>
		<security.version>4.2.3.RELEASE</security.version>
	</properties>

 

pom.xml 상단에 <properties>태그안에 <security.version>4.2.3.RELEASE</security.version>를 추가해줍니다.


 

 

 

 

2. web.xml

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:egovframework/spring/context-*.xml</param-value>
	</context-param>

 

spring폴더 안에 context- 로 시작하는 xml을 읽을수 있게 수정해줍니다.

 


 

 

 

 

3. context-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	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
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">


	<context:component-scan base-package="egovframework.example.security"/>

	<http auto-config="true" use-expressions="true">

		<!-- 모든 url 패턴에 ROLE_USER의 권한을 가지고 있을때만 접근가능 -->
		<!-- <intercept-url pattern="/**" access="ROLE_USER" /> -->
		<intercept-url pattern="/userPage/**" access="hasRole('ROLE_USER')"/>
		<intercept-url pattern="/**" access="permitAll"/>
		<access-denied-handler ref="userDeniedHandler" />


		<form-login
			username-parameter="loginId"
			password-parameter="loginPwd"
			login-processing-url="/login"
			login-page="/loginPage.do"
			default-target-url="/"
		/>

		<!-- 로그아웃할 url 및 로그아웃성공시 이동할 url -->

		<logout
			logout-url="/logout"
			logout-success-url="/"
			invalidate-session="true"
			delete-cookies="true" />

		<remember-me data-source-ref="dataSource"
            remember-me-parameter="remember-me"
            token-validity-seconds="604800"/>

         <!-- <remember-me services-ref="" /> -->
	</http>

	<beans:bean id="userDeniedHandler" class="egovframework.example.security.UserDeniedHandler"></beans:bean>

	<authentication-manager>
		<authentication-provider ref="userAuthProvider"></authentication-provider>
		<authentication-provider user-service-ref="userService"></authentication-provider>
	</authentication-manager>

	<beans:bean id="userService" class="egovframework.example.sample.service.CustomUserDetailsService"/>
	<beans:bean id="userAuthProvider" class="egovframework.example.security.CustomAuthenticationProvider"/>

</beans:beans>

 

web.xml에서 context- 관련 xml을 읽을수 있게 수정한 폴더에 context-security.xml 를 생성후 코드를 붙여줍니다.

 

<remember-me data-source-ref="dataSource"
            remember-me-parameter="remember-me"
            token-validity-seconds="604800"/>

이 코드에서 dataSource는 bean으로 등록된 dataSource id입니다. egov기준 context-datasource.xml

 

맨위 <context:component-scan base-package="egovframework.example.security"/> 스캔 가능하도록 설정한것 처럼

egovframework.example경로에  security 폴더를 생성합니다.

 

security 폴더안에 UserDeniedHandler.java와 CustomAuthenticationProvider.java 파일을 생성합니다. 

egovframework.example.sample.service  경로에 CustomUserDetailsService.java를 생성합니다.(로그인 관련 db 조회)


 

 

 

4. CustomAuthenticationProvider.java

package egovframework.example.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;

import egovframework.example.sample.service.UserVO;

public class CustomAuthenticationProvider implements AuthenticationProvider {

	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		// TODO Auto-generated method stub

		System.out.println("authentication : " + authentication);

		String loginUserName = String.valueOf(authentication.getPrincipal());
		String loginPassword = String.valueOf(authentication.getCredentials());
		System.out.println("loginUserName : " + loginUserName);
		System.out.println("loginPassword : " + loginPassword);

		UserVO user = (UserVO) userDetailsService.loadUserByUsername(loginUserName);

		if(!matchPassword(loginPassword, user.getPassword())) {

			System.out.println();

			throw new BadCredentialsException(loginUserName);
		}

		if(!user.isEnabled()) {
			throw new BadCredentialsException(loginUserName);
		}


		return new UsernamePasswordAuthenticationToken(loginUserName, loginPassword, user.getAuthorities());
	}

	@Override
	public boolean supports(Class<?> authentication) {
		// TODO Auto-generated method stub
		return true;
	}

	private boolean matchPassword(String loginPassword, String password) {

		return loginPassword.equals(password);

	}


}

 

 

 

5. UserDeniedHandler.java

package egovframework.example.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.AccessDeniedHandler;

public class UserDeniedHandler implements AccessDeniedHandler {

	private String errorPage = "/WEB-INF/jsp/egovframework/example/sample/errorPage.jsp";

	@Override
	public void handle(HttpServletRequest req, HttpServletResponse res,
			  AccessDeniedException ade) throws IOException, ServletException {
		// TODO Auto-generated method stub
		/*
		 * req.setAttribute("errMsg",ade.getMessage()); req.getRequestDispatcher(
		 * "/WEB-INF/jsp/egovframework/example/sample/errorPage.jsp").forward(req, res);
		 */


		String ajaxHeader = req.getHeader("X-Ajax-call");
		String result = "";

		res.setStatus(HttpServletResponse.SC_FORBIDDEN);
		res.setCharacterEncoding("UTF-8");

		// null로 받은 경우는 X-Ajax-call 헤더 변수가 없다는 의미이기 때문에
		// ajax가 아닌 일반적인 방법으로 접근했음을 의미한다.
		if(ajaxHeader == null) {

			Authentication auth = SecurityContextHolder.getContext().getAuthentication();
			Object principal = auth.getPrincipal();

			req.setAttribute("username", principal);
			req.setAttribute(("errormsg"), ade);
			req.getRequestDispatcher(errorPage).forward(req, res);


			result = "{\"result\" : \"fail\", \"message\" : \"" + ade.getMessage() + "\"}";
			System.out.println("result :::: " + result);

		}else {

			if("true".equals(ajaxHeader)) {
				// true로 받았다는 것은 ajax로 접근
				result = "{\"result\" : \"fail\", \"message\" : \"" + ade.getMessage() + "\"}";

			}else {
				// 헤더 변수는 있으나 값이 틀린 경우이므로 헤더값이 틀렸다는 의미로 돌려준다
				result = "{\"result\" : \"fail\", \"message\" : \"Access Denied(Header Value Mismatch)\"}";

			}

			res.getWriter().print(result);
			res.getWriter().flush();

		}

	}
	public void setErrorPage(String errorPage) {
		if ((errorPage != null) && !errorPage.startsWith("/")) {
			throw new IllegalArgumentException("errorPage must begin with '/'");
		}
		this.errorPage = errorPage;
	}



}





 

 

 

6. CustomUserDetailsService.java

package egovframework.example.sample.service;

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 egovframework.example.sample.service.impl.UserMapper;

public class CustomUserDetailsService implements UserDetailsService{

	@Autowired
	private UserMapper userMapper;

	@Override
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
		// TODO Auto-generated method stub

		UserVO user = userMapper.getUserById(userName);

		if(user == null) {
			throw new UsernameNotFoundException(userName);
		}

		return user;
	}


}

 

 

 

7. UserVO.java

package egovframework.example.sample.service;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserVO implements UserDetails {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

	private String userId;
	private String userPassword;
	private String userName;
	private String userAuthority;
	//private ArrayList<GrantedAuthority> authorities; 여러 권한?
	private boolean userEnabled;


	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		// TODO Auto-generated method stub
		ArrayList<GrantedAuthority> auth = new ArrayList<GrantedAuthority>();
        auth.add(new SimpleGrantedAuthority(userAuthority));
        return auth;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return userPassword;
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return userId;
	}

	@Override
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return userEnabled;
	}

	public String getName() {
		// TODO Auto-generated method stub
		return userName;
	}

	public void setNAME(String name) {
		userName = name;
	}

}

 

 

 

8. UserMapper.java

package egovframework.example.sample.service.impl;

import egovframework.example.sample.service.UserVO;
import egovframework.rte.psl.dataaccess.mapper.Mapper;

@Mapper("userMapper")
public interface UserMapper {

    public UserVO getUserById(String userName);

}

 

 

9. DB 테이블 생성

CREATE TABLE TB_USER (
  USER_ID VARCHAR(100) NOT NULL,
  USER_PASSWORD VARCHAR(300) NOT NULL,
  USER_NAME VARCHAR(45) NOT NULL,
  USER_AUTHORITY VARCHAR(50)DEFAULT 'ROLE_USER' NOT NULL,
  USER_ENABLED VARCHAR(1) DEFAULT '1' NOT NULL,
  PRIMARY KEY (USER_ID)
);


INSERT INTO TB_USER (USER_ID, USER_PASSWORD, USER_NAME, USER_AUTHORITY, USER_ENABLED)
VALUES('stage011', '1234', 'kyj', 'ROLE_USER', '1');

INSERT INTO TB_USER (USER_ID, USER_PASSWORD, USER_NAME, USER_AUTHORITY, USER_ENABLED)
VALUES('stage018', '1234', 'kyh', 'ROLE_GUEST', '1');


CREATE TABLE persistent_logins (
    username varchar(64) not null,
    series varchar(64) not null,
    token varchar(64) not null,
    last_used timestamp not null,
    PRIMARY KEY (series)
);


COMMIT;

 

 

 

10. userMapper.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="egovframework.example.sample.service.impl.UserMapper">

	<resultMap id="resultUserVO"
		type="egovframework.example.sample.service.UserVO">
		<result property="userId" column="USER_ID" />
		<result property="userPassword" column="USER_PASSWORD" />
		<result property="userName" column="USER_NAME" />
		<result property="userAuthority" column="USER_AUTHORITY" />
		<result property="userEnabled" column="USER_ENABLED" />
	</resultMap>
	<select id="getUserById" parameterType="String" resultMap="resultUserVO">
		SELECT
			  USER_ID
			, USER_PASSWORD
			, USER_NAME
			, USER_AUTHORITY
			, USER_ENABLED
		FROM TB_USER
		WHERE USER_ID = #{userId}
	</select>

</mapper>

 

 

 

11. LoginController.java

package egovframework.example.sample.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class LoginController {

	@RequestMapping(value="/loginPage.do")
	public String loginPage() throws Exception{
		return "sample/loginPage";
	}

	@RequestMapping(value="/userPage/userTest.do")
	public String userPage() throws Exception{
		return "sample/userPage";
	}

}

 

 

 

12. index.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<jsp:forward page="/main.do"/>

 

 

 

13. main.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	<p>현재시간 ${serverTime}</p>

	<!-- 로그인중이 아닐 때에만 Login 버튼이 보임  -> taglib ( security/tags ) 때문에 가능 -->
	<sec:authorize access="isAnonymous()">
 		<a href='${pageContext.request.contextPath}/loginPage.do'>Login</a>
	</sec:authorize>

	<!-- 로그인 중일 경우에만 Logout 버튼이보임 -->
	<sec:authorize access="isAuthenticated()">
		<form action="${pageContext.request.contextPath}/logout" method="POST">
			<input id="logoutBtn" type="submit" value="Logout" />
			<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
		</form>
	</sec:authorize>
	<form action="${pageContext.request.contextPath}/userPage/userTest.do" method="POST">

		<input id="logoutBtn" type="submit" value="userPage" />
		<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
	</form>
</body>
</html>

 

 

 

14. loginPage.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/login" method="post">

    <input type="text" name="loginId" placeholder="ID">

    <input type="password" name="loginPwd" placeholder="Password">

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

    <input name="remember-me" type="checkbox" />자동 로그인

    <button type="submit">로그인</button>
</form>


</body>
</html>

 

 

 

 

15. errorPage.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
권한이없다!
${username}
${errormsg}
</body>
</html>

 

 

 

 

16. userPage.jsp

<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"   uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="ui"     uri="http://egovframework.gov/ctl/ui"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

	userPage
</body>
</html>

 

 

 

 

17. 실행결과

http://localhost:7070/security/loginPage.do

 

http://localhost:7070/security/loginPage.do로 접속


 

 

 

로그인

 

사용자를 생성한 아이디로 자동 로그인 체크를 하고 로그인을 해본다.


 

 

 

자동로그인 token

 

 

자동로그인을 체크한 후 로그인하면 해당 테이블에 자동으로 INSERT 되며 로그아웃하면 테이블에서 데이터가 삭제된다. 


 

 

 

 

 

 

TB_USER 테이블에 권한 컬럼(USER_AUTHORITY) 가 ROLE_USER이면 해당 URL에 접근이 가능하고 아니면

사진과 같이 권한이없다는 내용의 errorPage.jsp 로 이동된다.

 

 

참고

https://codevang.tistory.com/274

 

스프링 Security_로그인_자동 로그인(Remember-me) [8/9]

- Develop OS : Windows10 Ent, 64bit - WEB/WAS Server : Tomcat v9.0 - DBMS : MySQL 5.7.29 for Linux (Docker) - Language : JAVA 1.8 (JDK 1.8) - Framwork : Spring 3.1.1 Release - Build Tool : Maven 3.6..

codevang.tistory.com

https://codevang.tistory.com/268

 

스프링 Security_로그인_로그인 실패 대응 로직 [3/9]

- Develop OS : Windows10 Ent, 64bit - WEB/WAS Server : Tomcat v9.0 - DBMS : MySQL 5.7.29 for Linux (Docker) - Language : JAVA 1.8 (JDK 1.8) - Framwork : Spring 3.1.1 Release - Build Tool : Maven 3.6..

codevang.tistory.com

https://velog.io/@hellas4/Security-관련-설정

 

Security 기본 원리 파악하기 (실습편)

기본 용어 접근 주체 ( Principal ) : 보호중인 대상에 접근하려는 유저 인증 ( Authenticate ) : 해당 유저가 누구인지 검증하는 절차 ID, PW 등을 이용한 로그인을 통하여 검증 해당 Application을 이용가능

velog.io

 

이 글을 공유합시다

facebook twitter googleplus kakaostory naver