프로젝트

7. 로그인

uni5948 2021. 11. 22. 22:02

7. 로그인

  • 서비스 생성

서비스 생성

더보기
package com.example.security;
import java.util.Collection;
import com.example.entity.Member;
import com.example.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class MemberDetailsService implements UserDetailsService{

    @Autowired
    private MemberRepository mRepository;

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

        Member member = mRepository.findById(username).get();
        //String의 권한을 collection<>으로 변환
        String[] role = {member.getUserrole()};
        Collection<GrantedAuthority> roles = AuthorityUtils.createAuthorityList(role);

        //이메일, 암호, 권한 정보를 리턴
        //import org.springframework.security.core.userdetails.User;
        User user = new User(member.getUserid(), member.getUserpw(), roles);
        return user;
    }
   
}
  • jwt 추가(토큰 생성 및 유효성 확인)
    jwt 추가
더보기
package com.example.jwt;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.stereotype.Service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

//토큰발행(만료시간), 토큰의 유효성 검사, 토큰과 관련된 메소드
@Service
public class JwtUtil {

    //토큰 생성용 보안키
    private final String SECRETKEY = "asdfjk";
   
     //정보 추출용 메소드
     private<T> T extractClaim(String token, Function<Claims, T> claimsResolver){
        final Claims claims = Jwts.parser().setSigningKey(SECRETKEY).parseClaimsJws(token).getBody();
        return claimsResolver.apply(claims);
    }

    //토큰생성(아이디 정보를 이용한 토큰 생성)
    public String generateToken(String username){
        // long tokenValidTime = 1000 * 60 * 30;   //30분
        long tokenValidTime = 1000 * 60 * 60 * 4; //4시간
        Map<String, Object> claims = new HashMap<>();
        String token =Jwts.builder()
            .setClaims(claims)
            .setSubject(username)
            .setIssuedAt(new Date(System.currentTimeMillis()))  //현재 시간
            .setExpiration(new Date(System.currentTimeMillis() + tokenValidTime))   //만료시간
            .signWith(SignatureAlgorithm.HS256, SECRETKEY)
            .compact();
        return token;
    }

     //토큰 유효성 확인
    // vue에서 오는 토큰에서 꺼낸 아이디 정보와
    // 시큐리티 세션에 보관되어 있던 아이디 정보
    public Boolean validateToken(String token, String userid){
        //토큰에서 아이디 정보 추출
        final String username = this.extractUsername(token);
        //vue에서 오는 토큰에서 꺼낸 아이디 정보와
        //시큐리티 세션에 보관되어 있던 아이디 정보
        if(username.equals(userid) && !isTokenExpired(token) ){
            return true;
        }
        return false;
    }

    //토큰에서 아이디 정보 추출하기
    public String extractUsername(String token){
        return extractClaim(token, Claims::getSubject);
    }

    //토큰에서 만료 시간 추출하기
    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
   
        //토큰의 만료시간이 유효한지 확인
        public Boolean isTokenExpired(String token){
            //만료시간 가져와서 현재시간보다 이전인지 확인
            return this.extractExpiration(token).before(new Date());
        }
}
  • JwtRequestFillter.java(토큰 확인)
더보기
package com.example.jwt;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.example.security.MemberDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class JwtRequestFilter extends OncePerRequestFilter{

    @Autowired
    JwtUtil jwtUtil;
    @Autowired
    MemberDetailsService mService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try{
            String token = request.getHeader("token");
            String username=null;
            if(token != null){
                //실제 토큰
                username = jwtUtil.extractUsername(token);
            }
            //username과 시큐리티 세션에 있는 사용자 아이디와 비교
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            //토큰은 전달외었고 로그인이 되어야 되는 것만
            if(username != null && auth == null ) {
                //시큐리티에 로그인 처리 루틴
                UserDetails userDetails = mService.loadUserByUsername(username);
                if(jwtUtil.validateToken(token, userDetails.getUsername()) ){
                    UsernamePasswordAuthenticationToken
                    upat = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                    upat.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request) );
                    SecurityContextHolder.getContext().setAuthentication(upat);
                }
            }
            //컨트롤러로 넘어가는 시점
            filterChain.doFilter(request, response);
        }
        catch(Exception e){
            e.printStackTrace();
            response.sendError(578,"토큰오류");
        }
    }    
}
  • db 연동(SecurityConfig.java)
package com.example.security;

import com.example.jwt.JwtRequestFilter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration  //환경설정 파일
@EnableWebSecurity  //security를 적용
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    MemberDetailsService mService;
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

     //환경 설정에서 객체만들기
    //회원가입시 암호 방식을 로그인 시 같은 방식으롤 적용해야 하기 때문에
    @Bean
    public BCryptPasswordEncoder encode(){
        return new BCryptPasswordEncoder();
    }

    //인증방식은 MemberDetailsService를 사용하고,
    //암호화 방식은 위에서 만든 @Bean 객체 방식으로 사용
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(mService).passwordEncoder(encode());
    }

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //권한 설정
        http.authorizeRequests()
            //관리자 로그인 -> 관리자 페이지는 ROLE=ADMIN 만 접속할 수 있다.
            .antMatchers("/admin","/admin/*").hasAuthority("ADMIN")
            //회원 로그인
            .antMatchers("/member","/member/*").permitAll().and();
        //필터 추가하기
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        //session 저장 방법
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //h2 console 사용
        http.csrf().disable();
        http.headers().frameOptions().sameOrigin();
    }
}
  • 로그인(MemberController.java)
    @Autowired
    JwtUtil jwtUtil;

    @Autowired
    AuthenticationManager authenticationManager;
    //로그인
    //127.0.0.1:8080/REST/member/login
    @RequestMapping(value = "member/login", method = {RequestMethod.POST},
    consumes = MediaType.ALL_VALUE,
    produces = MediaType.APPLICATION_JSON_VALUE)
    public Map<String, Object> loginPOST(@RequestBody Member member) {
        Map<String,Object> map = new HashMap<String, Object>();
        try {
            authenticationManager
                .authenticate(new UsernamePasswordAuthenticationToken(
                    member.getUserid(), member.getUserpw()));
            map.put("result", 1L);
            map.put("token", jwtUtil.generateToken(member.getUserid()));
        } catch (Exception e) {
            e.printStackTrace();
            map.put("result", e.hashCode());
        }
        return map;
    }
  • 로그인 구현

로그인 구현

 

'프로젝트' 카테고리의 다른 글

9. 팀, 에이전트 등록  (0) 2021.11.24
8. 회원 정보 수정  (0) 2021.11.23
6. 회원가입  (0) 2021.11.18
5. db 연동  (0) 2021.11.18
4. 보안 해제  (0) 2021.11.18