Spring 에서 xml을 이용하여 기존의 POST로 진행하는 Login 기능에다가

GET으로 Login진행할 수 있는 기능을 추가하여 사용할 수 있는 방법이다.

(따라서, 일반적인 Form 로그인 방식이 먼저 필요하다.)

2019/11/02 - [Develop/Spring] - Spring security 로그인(DB에 있는 아이디 조회)

 

Spring security 로그인(DB에 있는 아이디 조회)

1. 먼저 필요한 Maven을 설치합니다. 아래의 메이븐저장소에 들어가서 spring-security-core, spring-security-web, spring-security-config, pring-security-taglibs 검색하여 pom.xml에 추가합니다. https://mvn..

wky.kr

 

1. Filter 생성

기존의 Form Login방식은 POST로 데이터를 받기 때문에, Filter를 생성한다.

Username과 Password를 받을 것이기 때문에 UsernamePasswordAuthenticationFilter를 상속받는다.

 

public class GetLoginFilter extends UsernamePasswordAuthenticationFilter {

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		// TODO Auto-generated method stub
		return super.attemptAuthentication(request, response);
	}


}

 

2. Provider 생성

사실 기존에 POST로 로그인하는 기능이 존재하기 때문에 굳이 작성을 하지 않아도 되나, 혹시 GET방식에서 패스워드가 없어도 아이디만 있으면 로그인을 성공시키거나 이러한 기능을 하고 싶다면 생성을 해야한다.

 

public class GetLoginProvider implements AuthenticationProvider {

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

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

 

3. security xml에 Filter, Provider 등록

3.1 Filter 등록

filter 연결을 위해 class에 위에 생성한 GetLoginFilter를 연결해준다.

 

filterProcessesUrl을 등록해 Get방식으로 Login할 때 어떤 URL을 사용할지 등록한다.

 

name="authenticationManager" 에 기존의 POST방식의 인증을 사용하고 싶다면 ref="org.springframework.security.authenticationManager" 로 사용하면 되나,

여기서는 Provider를 만들었기 때문에 위에서 생성한 Provider를 getLogin이라는 이름으로 등록해 사용할 것이다.

 

인증성공핸들러와 인증실패핸들러는 기존에 이미 사용하고 있던 것이 있기 때문에 기존의 것을 사용해줘도 무관하거나 새로 만들어주어도 상관없다. (현재 글 작성 기준으로 이미 기존의 Form Login 방식이 있는 상태에서 Get Login을 설명하고 있기 때문)

 

postOnly는 Get으로 받을 것이기 때문에 false로 변경한다.

	<beans:bean id="GetLoginFilter" class="net.a.test.login.GetLoginFilter">
		<beans:property name="filterProcessesUrl" value="/getLogin" />
		<!-- <beans:property name="authenticationManager" ref="org.springframework.security.authenticationManager" /> -->
        <beans:property name="authenticationManager" ref="getLogin" />
		<beans:property name="usernameParameter" value="id" />
		<beans:property name="passwordParameter" value="password" />
		<beans:property name="authenticationSuccessHandler" ref="loginSuccessHandler" />
		<beans:property name="authenticationFailureHandler" ref="loginFailureHandler" />
		<beans:property name="postOnly" value="false"/>
	</beans:bean>

 

3.2 Provider 등록

위의 Filter에서 Provider를 등록하기 위해 Provider 또한 등록한다.

먼저, 아까 위의 2번에서 생성한 Provider를 bean으로 등록한다.

다음, 다시 등록한 bean을 authentication-manaer로 등록한다.

<beans:bean id="GetLoginProvider" class="net.a.test.login.GetLoginProvider" />

<authentication-manager id ="getLogin">
	<authentication-provider ref="GetLoginProvider" />
</authentication-manager>

 

이렇게 Filter와 Provider를 등록하면 Get 방식으로 인증이 가능해진다.

 

4. Provider 수정

3.1의 Filter 등록의 authenticationManager에서 기존의 인증방식이아닌 새로 생성한 Provider 인증방식을

등록하였기 때문에 2번에서 생성한 Provider를 수정해주어야 한다.

 

기존의 Form Login 기능에 사용하던 userDetailsService와 , PasswordEncoder를 가져온다.

 

authenticate 함수에서 Get 파라미터로 가져온 name과 password를 입력한다.

다음, 기존에 사용하던 loadUserByUsername을 통해 DB에 저장되어있는 암호화된 password와 권한들을 가져온다.

 

그리고 passwordEncoder의 matchs 기능으로 Get파라미터로 가져온 평문의 password와 암호화된 password를

비교해 Passoword가 맞다면 인증을 성공시키고, 다르다면 Exception을 던진다.

public class GetLoginProvider implements AuthenticationProvider {

	@Autowired
	UserDetailsService userDetailsService;
	
	@Autowired
	PasswordEncoder passwordEncoder;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		// TODO Auto-generated method stub
		String name = authentication.getName();
		String password = authentication.getCredentials().toString();
		UserDetails userDetails = userDetailsService.loadUserByUsername(name);

		Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();

		if(passwordEncoder.matches(password, userDetails.getPassword()))
			return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(),
				authorities);
		else
			throw new  BadCredentialsException("authentication failed");

	}

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

}

 

만약, 패스워드입력필요없이 아이디만으로 로그인하고 싶다면 아래와 같이 패스워드 비교 코드를 삭제하고 작성하면 된다.

public class GetLoginProvider implements AuthenticationProvider {

	@Autowired
	userDetailsService userDetailsService;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		// TODO Auto-generated method stub
		String name = authentication.getName();

		UserDetails userDetails = userDetailsService.loadUserByUsername(name);
	
		Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();

		// use the credentials
		// and authenticate against the third-party system
		return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(),
				authorities);

	}

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

}

 

 

이러한 방식으로 기존의 POST로 진행하던 Form Login에서 Get Login 방식을 추가할 수 있다.

'Develop > Spring' 카테고리의 다른 글

Spring Security URL login  (0) 2021.01.22
Spring Tomcat JNDI 설정  (0) 2020.11.17
Spring Security Custom Filter  (0) 2020.11.09
Spring security 로그인(DB에 있는 아이디 조회)  (0) 2019.11.02
Spring Mybatis + MariaDB(HikariCP) 설정  (0) 2019.10.24
Spring 3 에서 4로 migration  (0) 2019.10.22

Spring Boot 에서 Security Form Login 적용 방법이다.

먼저, 간단한 DB, 의존성 세팅과 DB연결 후 Security를 적용한다.

 

1. 기본 세팅

1.1 DB설정

로그인에 사용할 DB테이블을 먼저 생성한다. 

CREATE TABLE `user` (
	`id` VARCHAR(50) NOT NULL,
	`pw` VARCHAR(255) NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)

 

1.2 dependency 설정

DB는 현재 MariaDB와 JPA를 사용하며, spring security 의존성과 함께 추가한다.

implementation 'mysql:mysql-connector-java'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'

 

1.3 DB 연결 설정

application.yml(properties) 파일에 DB 연결 정보를 추가 한다.

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: "jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Seoul"
    username:
    password:
  jpa:
    hibernate:
      ddl-auto: update
    generate-ddl: false
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    properties: 
      hibernate:
        format_sql: true

 

2. JPA 적용

Jpa는 Entity와 Interface로 이루어진 Repository를 생성하면 간단하게 적용할 수 있다.

2.1 Entity 생성

UserVO를 생성하여 생성한 DB테이블에 맞게 VO에 Entity와 Column명 들을 설정한다.

@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "user")
public class UserVO {

	@Id
	@Column(name = "id")
	private String id;
	
	@Column(name = "pw")
	private String pw;
	
	@Column(name = "name")
	private String name;
}

 

2.2 JpaRepository 생성

VO로 생성한 table을 연결할 JpaRepository를 생성한다.

public interface UserRepository extends JpaRepository<UserVO, String> {
	
}

 

3. Security 적용

3.1 WebSecurityConfigurerAdapter 적용

먼저 WebSecurityConfigurerAdapter를 상속받고, 아래 2개의 메소드를 Override하고 Bean을 등록한다.

 

1. configure(HttpSecurity http) 메소드는 기본적인 security 기본 설정을 하는 것이다. 현재는 csrf를 비활성화 시키고,

authorizeRequests()를 이용하여 요청에대한 권한을 처리한다. /login 경로는 권한이 없어도 접속을 허용하고, anyRequest().authenticated()를 통해 이 외의 경로는 권한이 있어야 접속을 허용한다.

formLogin() 을 통해 formLogin을 사용하겠다는 것이며, formLogin 페이지를 만들지 않았다면, Security에서 제공해주는 로그인페이지를 사용한다. 만약 로그인페이지가 있다면. formLogin().loginPage("로그인페이지 경로") 라는 loginPage 옵션을 추가하여 로그인페이지를 커스텀페이지로 변경할 수 있다.

formLogin에는 로그인 성공시 작동할 기능, 실패시 작동할 기능, 로그인 인증 URL 변경 등 다양한 기능들이 있다.

 

2. configure(AuthenticationManagerBuilder auth) 메소드는 인증방법을 의미하며,

Spring에서 xml로 Security 설정시 <authentication-manager> <authentication-provider>를 설정하는 것과 같다. 예를 들면 DB에서 사용자 정보를 불러와 현재 입력된 정보와 비교 후 UserDetails 을 반환하여 인증하겠다 라는 의미이다.
현재는 CustomUserDetailSerivce 객체에 인증 방법이 있으며, 패스워드 인코더를 설정해 두었다.

 

3. PasswordEncoder는 로그인시 DB에 BCrpyt로 저장되어있는 패스워드를 로그인할 때 입력한 비밀번호와 비교하기 위해 사용되며, 이는 인증할 때 사용되는 configure(AuthenticationManagerBuilder auth) 메소드에 사용될 것이다.

 

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private CustomUserDetailService userDetailsService;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		http
			.csrf().disable()
			.authorizeRequests()
				.antMatchers("/login").permitAll()
				.anyRequest().authenticated()
			.and()
			.formLogin();
	}
	
    @Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
    	auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

 

3.2 인증을 처리할 메소드

아이디와 비밀번호를 통해 인증을 직접적으로 처리할 메소드이다.

UserDetailsService를 implements하여 loadUserByUsername을 Override한다.

권한을 임의로 생성하였으며,

2번에서 생성한 Repository를 통해 userRepository.findById(로그인아이디)를 사용하여, DB에 저장되어있는 아이디와 비밀번호를 불러온다.

불러온후 security.core에서 제공해주는 User객체를 생성하여 반환한다.

 

configure(AuthenticationManagerBuilder auth)에서 설정한 BCrpyt PasswordEncoder에는 macth라는 패스워드 비교하는 기능이 있는데 User 객체를 반환하면서 현재의 비밀번호와 폼에서 입력한 비밀번호가 match 기능으로 인하여 비밀번호를 비교해주고 비밀번호가 올바르면 로그인 인증 성공으로 처리하고 틀리다면 실패로 처리한다.

import org.springframework.security.core.userdetails.User;

@Service
public class CustomUserDetailService implements UserDetailsService {

	@Autowired
	private UserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		
        Collection<SimpleGrantedAuthority> roles = new ArrayList<SimpleGrantedAuthority>();
		roles.add(new SimpleGrantedAuthority("ROLE_USER"));
		
        UserVO userVO =userRepository.findById(username).orElseThrow(() -> new NoSuchElementException());
		return new User(userVO.getId(), userVO.getPw(), roles);
		
	}

}

 

 

이러한 방법을 이용하여, 생성한 DB User테이블에 임의의 ID와 BCrpyt로 암호화한 패스워드를 넣고,

서버를 실행하여 /login 경로로 접속하면, Spring Security에서 기본으로 제공하는 Login Form을 이용하여 로그인을 할 수 있다.

 

1. 먼저 필요한 Maven을 설치합니다.

아래의 메이븐저장소에 들어가서 spring-security-core, spring-security-web, spring-security-config, pring-security-taglibs 검색하여 pom.xml에 추가합니다.

https://mvnrepository.com/

 

Maven Repository: Search/Browse/Explore

Reactivewizard Binding Last Release on Oct 31, 2019

mvnrepository.com

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>4.2.4.RELEASE</version>
		</dependency>

 

2. web.xml 에 들어가 아래와 같이 추가합니다.

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/spring/root-context.xml
		/WEB-INF/spring/security-context.xml
	</param-value>
</context-param>

<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>

 

3. 로그인 창을 하나 만듭니다.

파일이름은 login.jsp로 만들었습니다.

<!-- login.jsp -->
<form action="loginProcess" method="post">
<input type="text" name="userId">
<input type="password" name="userPw">
<input type="submit" value="ok">
</form>

 

4. /WEB-INF/spring 폴더 밑에 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"

	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.xsd">


	<security:http auto-config='true' use-expressions="true">
		<security:csrf disabled="true" />
        
		<security:intercept-url pattern="/login"
			access="permitAll" />

		<security:intercept-url pattern="/**"
			access="isAuthenticated()" />

		<security:form-login login-page="/login"
			username-parameter="userId" password-parameter="userPw"
			login-processing-url="/loginProcess" default-target-url="/test"
			authentication-failure-url="/login?failure"
			always-use-default-target='true' />
            
		<security:logout invalidate-session="true"
			logout-url="/logout" logout-success-url="/login?logout" />
	</security:http>


</beans>

대충 설명하자면 /login 주소는 권한이없어도 접속을 허용하겠다는 뜻이고

그 외 주소는 로그인 인증이 되어야 접속할 수 있도록 하겠다는 뜻입니다.

그 외를 알고싶으시면 use-expressions(SpEL) 을 찾아보시면 됩니다.

그리고 3번에서 만든 로그인페이지를 등록하고 3번에서 만든 각각 id, passwordname들을 지정합니다.

또한 login-proccessing-url에 3번에서 만든 action 의 주소를 입력합니다. 여기서는 loginProcess로 되어있지만 아무거나 해도됩니다.

login-proccessing-url은 로그인 처리를 해주는 url인데 이름만 정해주면 security 측에서 알아서 만들어주는 url으로써  이름만 정하면 따로 저희가 할 일은 없이 spring security가 로그인처리를 진행 해줍니다.

default-target-url은 로그인성공시 접속할 주소를 입력합니다.

auauthentication-failure-url 은 로그인 실패시 처리할 주소를 입력하시면 됩니다.
always-use-default-target='true' 로그인 성공후 default-target-url에 설정한 곳으로 갈지 선택하는 것입니다.

invalidate-session="true" logout-url="/logout" logout-success-url="/login?logout" 로그아웃 입니다. 로그아웃 경로를 저장하고 로그아웃 성공시 돌아갈 경로를 저장해주면 로그아웃이 됩니다. 이 로그아웃url 또한 security 측에서 알아서 만들어주는 url으로써 여기서 이름만 정하면 따로 저희가 만들일 없이 spring security가 알아서 로그아웃을 진행해 줍니다.

 

5. 그리고 위의 security-context.xml 안에 로그아웃 밑에 바로 아래와 같이 추가합니다.

	<security:authentication-manager>

		<security:authentication-provider

			user-service-ref="loginService">

			<security:password-encoder

				ref="bcryptPasswordEncoder" />

		</security:authentication-provider>

	</security:authentication-manager>


	<bean id="loginService" class="net.test.test.service.testService" />

	<bean id="bcryptPasswordEncoder"

		class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

로그인 진행시 아이디를 가지고 비밀번호를 비교할 서비스클래스를 정하는 것입니다.

또한 데이터베이스에 비빌번호가 bcrypt로 저장되어 있을 경우 비밀번호 비교시 bcrypt로 비밀번호를 비교하기 위해 설정해줍니다.

 

6. 위에서 아이디를 가지고 비밀번호를 비교할 서비스를 생성합니다.

여기서 DB베이스에서 회원정보를 불러와 암호화되어있는 비밀번호를 비교하게됩니다.

public class testService implements UserDetailsService {

	@Autowired
	TestDAO testDAO;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		loginVO userVO = testDAO.selectLogin(username);

		if (userVO == null) {
			throw new UsernameNotFoundException("No user found with id");

		}

		Collection<SimpleGrantedAuthority> roles = new ArrayList<SimpleGrantedAuthority>();

		roles.add(new SimpleGrantedAuthority("ROLE_USER"));

		UserDetails user = new User(userVO.getId(), userVO.getPassword(), roles);
		return user;

	}

}

UserDetailsService라는 것을 implements합니다.

그리고 username을 통해 데이터베이스에서 회원정보를 가져오고

roles에 유저를 선택한 다음 UserDetails를 생성하여 return 만 해주면 Spring Security가 알아서 비밀번호를 데이터베이스에 있는 것과 form에서 넘어온 비밀번호를 비교하여 맞으면 로그인성공해주고 아니면 로그인실패를 해줍니다.(그냥 조회만 했을 뿐인데 알아서 다 해줌)

 

이렇게 bean설정과 UserDetails만 설정하면 로그인form에서 아이디하고 비밀번호를 전달한 뒤 아이디만 조회했을 뿐인데 spring security가 로그인처리까지 다 진행해줍니다.

다음 controller에서 Authentication을 통해 로그인한 정보(아이디)를 가져와 사용할 수 있습니다.

'Develop > Spring' 카테고리의 다른 글

Spring Security URL login  (0) 2021.01.22
Spring Tomcat JNDI 설정  (0) 2020.11.17
Spring Security Custom Filter  (0) 2020.11.09
Spring security 로그인(DB에 있는 아이디 조회)  (0) 2019.11.02
Spring Mybatis + MariaDB(HikariCP) 설정  (0) 2019.10.24
Spring 3 에서 4로 migration  (0) 2019.10.22

+ Recent posts