본문 바로가기
spring/게시판

스프링 시큐리티 예제

by coie 2021. 4. 7.

개인 프로젝트를 만들고 있는데

어제오늘 해서 

스프링 시큐리티로 로그인 회원가입

그리고 게시판 작성 리스트까지 완료하고

시큐리티 관련은 포스팅하면서 정리하는 것이 좋지 않을까

하고 작성한 코드를 예제로 설명하겠습니다.

 

먼저 security-context.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="kr.co.korea.dao"/>
	
	<http auto-config="true" use-expressions="true">
		<intercept-url pattern="/board/**" access="hasAnyRole('ROLE_MEMBER','ROLE_ADMIN')" />
		<intercept-url pattern="/**" access="permitAll" />
		
	
		<form-login
        username-parameter="loginid"
        password-parameter="loginpwd"
        login-processing-url="/loginok"
        login-page="/member/login"
        authentication-failure-handler-ref="loginFailHandler"
        authentication-success-handler-ref="loginSuccessHandler"
	    />
	    
	    <logout
        logout-url="/logout"
        logout-success-url="/"
	    />
 		<session-management>
 			<concurrency-control max-sessions="1"/>
 		</session-management>	
		 <csrf disabled = "true" /> 
	</http>

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


<beans:bean id="loginSuccessHandler" class="kr.co.korea.handler.LoginSuccessHandler">
	<beans:property name="loginidname" value="loginid" />
	<beans:property name="defaultUrl" value="/"/>
</beans:bean>

<beans:bean id="loginFailHandler" class="kr.co.korea.handler.LoginFailHandler">
	<beans:property name="loginidname" value="loginid"/>
	<beans:property name="loginpwdname" value="loginpwd"/>
	<beans:property name="defaultFailurl" value="/member/login?error"/>
</beans:bean>

<beans:bean id="userService" class="kr.co.korea.service.MemberAuthService" />
<beans:bean id="userAuthProvider" class="kr.co.korea.service.MemberProvider"/>


</beans:beans>

 

먼저 위에서부터 하나씩 설명을 하자면

 

context:component-scan -> dao 패키지를 scan 하는 것인데 가끔 시큐리티에서 못 읽는 경우가 있다.(경험...)

그래서 root-context에서 설정해뒀지만 한번 더 설정해두는 것.

http  여기서부터 시큐리티의 기본 적인 내용들이 담김.
intercept-url

말 그대로 인터셉터이다. access로 권한을 부여하는데

 hasRole(단일 admin만 접속 가능한 페이지)

 hasAnyRole(복수 권한을 부여한 여러 사람들이 접속 가능)

 permitAll 모든 사용자 로그인 필요 x

form-login 로그인 페이지로 인터셉터한다고 생각하면 편하다.
username-parameter  username이지만 우리가 알고 있는 id값
(form의 input의 name)
password-paramete  비밀번호 값
 login-processing-url   login 페이지의 form의 action 값
 authentication-failure-handler-ref   로그인을 실패했을 시 
authentication-success-handler-ref 로그인을 성공했을 시
  login-page 로그인하는 페이지

 

이 이외에도 여러 가지가 있는데 현재의 나에게 필요한 것은

이 정도...?라고 생각했다.

 

logout   logout 할 때
logout-url  logout 할 페이지
logout-success-url 로그아웃 성공했을 시(나의 경우에는 메인으로)
<session-management><concurrency-control max-sessions="1"/></session-management> 이 부분은 최대 로그인 가능한 수인데
즉, 다른 곳에서 로그인 시도 한 경우 사전에 로그인되어있으면 그것은 로그아웃.
authentication-manager 권한을 여기서부터 설정.
<authentication-provider ref="userAuthProvider"/> 시큐리티에서 값을 받아오면 그 값을 service로 보내고
해당 값이 일치하고 존재할 경우 그것을 토큰으로 넘기는 역할,
여기서 service로 넘어가기 때문에 위에 뒀다.

<authentication-provider user-service-ref="userService"/> 우리가 흔히 아는 Service 단지 UserDetailsService를 상속받아서 사용

그리고 아래의 beans:bean은 각 해당하는 부분의

handlere와 service, provider이다.

 

그다음은 bean부분

 

public class MemberAuthBean implements UserDetails{
	
	private String mid;
    private String mpwd;
    private String AUTHORITY;
    private boolean ENABLED;//사용여부
    private String mname;//이름
    private int mlevel;//사용자 권한 레벨
    private int mdrop;//사용자 탈퇴 여부
	
	public void setAUTHORITY(String aUTHORITY) {
		AUTHORITY = aUTHORITY;
	}

	public int getMlevel() {
		return mlevel;
	}

	public void setMlevel(int mlevel) {
		this.mlevel = mlevel;
	}

	public int getMdrop() {
		return mdrop;
	}

	public void setMdrop(int mdrop) {
		this.mdrop = mdrop;
	}

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

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

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

	public void setNAME(String nAME) {
		mname = nAME;
	}

	public void setENABLED(boolean eNABLED) {
		ENABLED = eNABLED;
	}
	
	@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 ENABLED;//계정 사용 가능한지 여부
	}

 

오버라이드 부분 이외에는

전부 개인이 정의를 해두면 된다.

나의 경우 권한을 level로 바로 설정해두었고

탈퇴 여부 도 알아야 해서 mdrop으로 설정.

 

그다음 memberservice

 

public class MemberAuthService implements UserDetailsService{
	
	@Autowired
	private MemberAuthDao memberdao;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		MemberAuthBean member = memberdao.getMeminfo(username);
		
		if(member==null) {
			throw new UsernameNotFoundException(username);
		}
		
		if(member.getMdrop()!=0) {
			member.setENABLED(false);
		}else {
			member.setENABLED(true);
		}
		
		if(member.getMlevel()==0) {
			member.setAUTHORITY("ROLE_MEMBER");
		}else {
			member.setAUTHORITY("ROLE_ADMIN");
		}
		
		return member;
	}

 

보통은 이렇게 하는가... 싶기도 한데

탈퇴 여부와 권한을 여기서 해결했다.

 

그리고 마지막 provider부분.

public class MemberProvider implements AuthenticationProvider{
	
	@Autowired
	private UserDetailsService userdservice;
	
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		// TODO Auto-generated method stub
		
			String username = (String) authentication.getPrincipal();//입력받은 아이디
	        String password = (String) authentication.getCredentials();//입력받은 비밀번호
	        
	        MemberAuthBean user = (MemberAuthBean) userdservice.loadUserByUsername(username);
	        
	        if(!matchPassword(password, user.getPassword())) {
	            throw new BadCredentialsException(username);
	        }
	 
	        if(!user.isEnabled()) {
	            throw new BadCredentialsException(username);
	        }
	        //토큰으로 ㄱ
	        return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
	}

	@Override
    public boolean supports(Class<?> authentication) {
        return true;
    }
    
    private boolean matchPassword(String loginPwd, String password) {
        return loginPwd.equals(password);
    }

 

 

만약 본인이 session에 id와 비밀번호 이외에도 여러 정보를 넘기고 싶으면

'토큰으로 ㄱ'하는 부분에 username이 아니라

bean을 그대로 넣어주면 된다. 그리고 password를 null로 설정 후

controller단에서 bean을 다시 설정해주면 된다.

 

처음에 시큐리티 할 때 몇일을 고생하다 보니

어느 정도의 구글링도 필요하지만

흐름이 대충 눈에 보여서

금방 한 것 같다

'spring > 게시판' 카테고리의 다른 글

스프링 게시판 좋아요 버튼  (0) 2021.04.24
스프링 Rest CRUD 구현  (0) 2021.04.07
스프링 이미지 업로드  (0) 2021.03.11
스프링 selectKey  (0) 2021.03.02
게시판 만들기 (댓글) ajax 정리  (0) 2021.02.23