250x250
Notice
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Eclipse
- ubuntu
- MSSQL
- SpringBoot
- maven
- git
- MySQL
- Core Java
- jpa
- PostgreSQL
- oracle
- STS
- Thymeleaf
- Python
- Tomcat
- JDBC
- Spring Boot
- AJAX
- JavaScript
- myBatis
- IntelliJ
- spring
- error
- 문서
- Open Source
- 설정
- Source
- Exception
- Docker
- 오픈소스
Archives
- Today
- Total
헤르메스 LIFE
[Spring Boot] Spring Boot + JWT ( JSON Web Token ) + DB 연결 본문
Spring Boot Framework
[Spring Boot] Spring Boot + JWT ( JSON Web Token ) + DB 연결
헤르메스의날개 2021. 1. 14. 02:10728x90
개발환경
Spring Boot 2.2.4
JDK 1.8.0_202
REST
Postman : REST Client
목표
1. Spring Boot REST 환경
2. Log4j2 추가
3. JWT + Spring Security 를 통한 인증
4. DB 연결 ( Hibernate 제거 )을 통한 사용자 인증
어쩌다 제가 원하는 프로젝트를 찾았습니다. 소스까지 오픈 해주셔서 제 입맛에 맞게 수정을 했습니다.
출처 : daddyprogrammer.org/post/636/springboot2-springsecurity-authentication-authorization/
아래의 내용은 위 소스를 바탕으로 수정한 내용입니다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rest</groupId>
<artifactId>SimpleRestAPI</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--
스프링 부트에서 Log4j2를 사용하기위해선 내부로깅에서 쓰이는 의존성을 제외해주어야 합니다.
기본적으로 Spring은 Slf4j라는 로깅 프레임워크를 사용합니다.
구현체를 손쉽게 교체할 수 있도록 도와주는 프레임 워크입니다.
Slf4j는 인터페이스고 내부 구현체로 logback을 가지고 있는데,
Log4j2를 사용하기 위해 exclude 해야 합니다.
-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- Log4j2 로깅 구현체를 사용을 위해 Spring Boot Starter에서 지원해주는 log4j2 의존성을 추가 해준다. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.9</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<!-- MyBatis 설정-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
<!-- Database - MSSQL -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Database - H2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Security and JWT -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- yaml-resource-bundle -->
<dependency>
<groupId>net.rakugakibox.util</groupId>
<artifactId>yaml-resource-bundle</artifactId>
<version>1.1</version>
</dependency>
<!-- com.google.code.gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<!-- 빌드 플러그인 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.rest.api.controller.v1;
import com.rest.api.entity.User;
import com.rest.api.model.response.CommonResult;
import com.rest.api.model.response.SingleResult;
import com.rest.api.service.SignService;
import com.rest.comm.advice.exception.CEmailSigninFailedException;
import com.rest.comm.config.security.JwtTokenProvider;
import com.rest.comm.service.ResponseService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
@Api(tags = {"1. Sign"})
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/v1")
public class SignController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private final JwtTokenProvider jwtTokenProvider;
private final ResponseService responseService;
private final PasswordEncoder passwordEncoder;
@Resource(name="signService")
private SignService signService;
@ApiOperation(value = "로그인", notes = "이메일 회원 로그인을 한다.")
@PostMapping(value = "/signin")
public SingleResult<String> signin(@ApiParam(value = "회원ID : 이메일", required = true) @RequestParam String id,
@ApiParam(value = "비밀번호", required = true) @RequestParam String password) {
Map<String, Object> param = new HashMap<String, Object>();
param.put("userId", id);
param.put("password", password);
User user = signService.getUser(param);
String endPass = passwordEncoder.encode(user.getPassword());
if (!passwordEncoder.matches(password, endPass)) throw new CEmailSigninFailedException();
return responseService.getSingleResult(jwtTokenProvider.createToken(String.valueOf(user.getMsrl()), user.getRoles()));
}
}
package com.rest.api.controller.v1;
import com.rest.api.entity.User;
import com.rest.api.model.response.CommonResult;
import com.rest.api.model.response.SingleResult;
import com.rest.api.service.SignService;
import com.rest.comm.advice.exception.CEmailSigninFailedException;
import com.rest.comm.config.security.JwtTokenProvider;
import com.rest.comm.service.ResponseService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
@Api(tags = {"1. Sign"})
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/v1")
public class SignController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private final JwtTokenProvider jwtTokenProvider;
private final ResponseService responseService;
private final PasswordEncoder passwordEncoder;
@Resource(name="signService")
private SignService signService;
@ApiOperation(value = "로그인", notes = "이메일 회원 로그인을 한다.")
@PostMapping(value = "/signin")
public SingleResult<String> signin(@ApiParam(value = "회원ID : 이메일", required = true) @RequestParam String id,
@ApiParam(value = "비밀번호", required = true) @RequestParam String password) {
Map<String, Object> param = new HashMap<String, Object>();
param.put("userId", id);
param.put("password", password);
User user = signService.getUser(param);
String endPass = passwordEncoder.encode(user.getPassword());
if (!passwordEncoder.matches(password, endPass)) throw new CEmailSigninFailedException();
return responseService.getSingleResult(jwtTokenProvider.createToken(String.valueOf(user.getMsrl()), user.getRoles()));
}
}
package com.rest.api.dao.mssql;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class MssqlRestDao {
@Autowired
@Qualifier("mssqlSqlSessionTemplate")
private SqlSessionTemplate sqlSession;
/* test 조회 */
public List<Map> selectList(Object param) {
return sqlSession.selectList("com.tbUser.select1", param);
}
}
package com.rest.api.dao.mssql;
import com.rest.api.entity.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class SignDao {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier("mssqlSqlSessionTemplate")
private SqlSessionTemplate sqlSession;
/* test 조회 */
public User getUser(Object param) {
User user = sqlSession.selectOne("v1.getUser", param);
return user;
}
}
package com.rest.api.entity;
import lombok.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.*;
import org.springframework.security.core.userdetails.*;
import java.util.*;
import java.util.stream.*;
@ToString
public class User implements UserDetails {
String msrl;
String password;
String username;
public String getMsrl() {
return msrl;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
public void setMsrl( String msrl ) {
this.msrl = msrl;
}
public void setPassword( String password ) {
this.password = password;
}
public void setUsername( String username ) {
this.username = username;
}
public List<String> getRoles() {
return roles;
}
public void setRoles( List<String> roles ) {
this.roles = roles;
}
private List<String> roles = new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream().map( SimpleGrantedAuthority :: new ).collect( Collectors.toList() );
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package com.rest.api.service;
import com.rest.api.entity.User;
public interface SignService {
public User getUser(Object param);
}
package com.rest.api.service;
import com.rest.api.dao.mssql.SignDao;
import com.rest.api.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("signService")
public class SignServiceImpl implements SignService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
SignDao signDao;
@Override
public User getUser(Object param) {
logger.debug("[SignServiceImpl >> getUser]");
return signDao.getUser(param);
}
}
package com.rest.comm.config.security;
import lombok.extern.slf4j.*;
import org.springframework.security.core.*;
import org.springframework.security.core.context.*;
import org.springframework.web.filter.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
@Slf4j
public class JwtAuthenticationFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
// Jwt Provier 주입
public JwtAuthenticationFilter( JwtTokenProvider jwtTokenProvider ) {
this.jwtTokenProvider = jwtTokenProvider;
}
/**
* <pre>
* Request로 들어오는 Jwt Token의 유효성을 검증(jwtTokenProvider.validateToken)하는 filter를 filterChain에 등록합니다.
* </pre>
*
* @param request
* @param response
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain filterChain ) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken( (HttpServletRequest) request );
if( token != null && jwtTokenProvider.validateToken( token ) ) {
Authentication auth = jwtTokenProvider.getAuthentication( token );
SecurityContextHolder.getContext().setAuthentication( auth );
}
filterChain.doFilter( request, response );
}
}
package com.rest.comm.config.security;
import io.jsonwebtoken.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Component
public class JwtTokenProvider { // JWT 토큰을 생성 및 검증 모듈
private static Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
@Value("${spring.jwt.secret}")
private String secretKey;
private long tokenValidMilisecond = 1000L * 60 * 60; // 1시간만 토큰 유효
private final UserDetailsService userDetailsService;
@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
// Jwt 토큰 생성
public String createToken(String userPk, List<String> roles) {
Claims claims = Jwts.claims().setSubject(userPk);
claims.put("roles", roles);
Date now = new Date();
return Jwts.builder()
.setClaims(claims) // 데이터
.setIssuedAt(now) // 토큰 발행일자
.setExpiration(new Date(now.getTime() + tokenValidMilisecond)) // set Expire Time
.signWith(SignatureAlgorithm.HS256, secretKey) // 암호화 알고리즘, secret값 세팅
.compact();
}
// Jwt 토큰으로 인증 정보를 조회
public Authentication getAuthentication(String token) {
UserDetails userDetails = userDetailsService.loadUserByUsername(this.getUserPk(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
// Jwt 토큰에서 회원 구별 정보 추출
public String getUserPk(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
/**
* <pre>
* Request의 Header에서 token 파싱 : "X-AUTH-TOKEN: jwt토큰"
* </pre>
*
* @param req
* @return
*/
public String resolveToken(HttpServletRequest req) {
return req.getHeader("X-AUTH-TOKEN");
}
/**
* <per>
* Jwt 토큰의 유효성 + 만료일자 확인
* </per>
*
* @param jwtToken
* @return
*/
public boolean validateToken(String jwtToken) {
try {
logger.debug( "[JwtTokenProvider >> validateToken]" );
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
return !claims.getBody().getExpiration().before(new Date());
} catch (SignatureException e) {
logger.error("Invalid JWT signature", e);
return false;
} catch (MalformedJwtException e) {
logger.error("Invalid JWT token", e);
return false;
} catch (ExpiredJwtException e) {
logger.error("Expired JWT token", e);
return false;
} catch (UnsupportedJwtException e) {
logger.error("Unsupported JWT token", e);
return false;
} catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty.", e);
return false;
}catch (Exception e) {
return false;
}
}
}
package com.rest.comm.config.security;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@RequiredArgsConstructor
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtTokenProvider jwtTokenProvider;
private static final String[] SWAGGER_URI = {
"/swagger-ui.html", "/v2/api-docs", "/swagger-resources/**", "/webjars/**", "/swagger/**"
};
private static final String[] PUBLIC_URI = {
"/*/signin", "/*/signin/**", "/*/signup", "/*/signup/**"
};
private static final String[] PUBLIC_GET_URI = {
"/exception/**", "/helloworld/**", "/actuator/health"
};
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable() // rest api 이므로 기본설정 사용안함. 기본설정은 비인증시 로그인폼 화면으로 리다이렉트 된다.
.csrf().disable() // rest api이므로 csrf 보안이 필요없으므로 disable처리.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt token으로 인증할것이므로 세션필요없으므로 생성안함.
.and()
.authorizeRequests() // 다음 리퀘스트에 대한 사용권한 체크(리소스 별 허용 범위 설정)
.antMatchers(PUBLIC_URI).permitAll() // 가입 및 인증 주소는 누구나 접근가능
.antMatchers(HttpMethod.GET, PUBLIC_GET_URI).permitAll() // 등록된 GET요청 리소스는 누구나 접근가능
//.antMatchers("/*/users").hasRole( "ADMIN" ) // '/users' api는 ROLE_ADMIN 만 가능함.
.anyRequest().hasRole("USER") // 그외 나머지 요청은 모두 인증된 회원만 접근 가능
.and()
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler()) // 인증 오류 발생 시 처리를 위한 핸들러 추가
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint()) // 인증 오류 발생 시 처리를 위한 핸들러 추가
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); // jwt token 필터를 id/password 인증 필터 전에 넣어라.
}
@Override // ignore swagger security config
public void configure(WebSecurity web) {
web.ignoring().antMatchers(SWAGGER_URI);
}
}
package com.rest.comm.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* <pre>
* 참조 : 다중 데이터베이스 설정 https://parandol.tistory.com/17
* 소스 : https://github.com/parandol/public-projects/blob/master/springboot.multi.database.example/src/main/java/multi/database/db1/dao/Db1Mapper.java
* </pre>
*/
@Configuration
@PropertySource("classpath:/application.yml")
@MapperScan(value = "multi.database.mssql.dao", sqlSessionFactoryRef = "mssqlSqlSessionFactory")
public class MssqlConfig {
private static final Logger logger = LoggerFactory.getLogger(MssqlConfig.class);
@Autowired
private ApplicationContext applicationContext;
@Value("${spring.mssql.datasource.mapper-locations}")
private String mapperLocations;
@Value("${spring.mssql.datasource.mybatis-config}")
private String configPath;
@Bean(name = "mssqlDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.mssql.datasource")
public DataSource mssqlDataSource() {
DataSource dataSource = DataSourceBuilder.create().build();
logger.info("[MssqlConfig >> mssqlDataSource] Datasource : {}", dataSource);
return dataSource;
}
@Bean(name = "mssqlSqlSessionFactory")
@Primary
public SqlSessionFactory mssqlSqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(mapperLocations));
//Mybatis config파일 위치
Resource myBatisConfig = new PathMatchingResourcePatternResolver().getResource(configPath);
sqlSessionFactoryBean.setConfigLocation(myBatisConfig);
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "mssqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mssqlSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
package com.rest.comm.service.security;
import com.rest.api.entity.User;
import com.rest.api.service.*;
import lombok.RequiredArgsConstructor;
import org.slf4j.*;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import javax.annotation.*;
import java.util.*;
@RequiredArgsConstructor
@Service
public class CustomUserDetailService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(CustomUserDetailService.class);
@Resource(name="signService")
private SignService signService;
public UserDetails loadUserByUsername(String userPk) {
Map<String, Object> param = new HashMap<String, Object>();
param.put("userId", userPk);
User result = signService.getUser(param);
List<String> list = new ArrayList<String>();
list.add("ROLE_USER");
list.add("ADMIN");
result.setRoles(list);
return result;
}
}
package com.rest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.client.RestTemplate;
/**
* <pre>
* 출처 : https://javaengine.tistory.com/entry/SpringBoot2%EB%A1%9C-Rest-api-%EB%A7%8C%EB%93%A4%EA%B8%B08-%E2%80%93-SpringSecurity-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B8%EC%A6%9D-%EB%B0%8F-%EA%B6%8C%ED%95%9C%EB%B6%80%EC%97%AC
* 출처 : https://github.com/codej99/SpringRestApi/tree/feature/security
* </pre>
*/
@SpringBootApplication(scanBasePackages = {"com.rest.api", "com.rest.comm"})
public class SpringRestApiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRestApiApplication.class, args);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
User_SqlMapper.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="v1">
<select id="getUser" parameterType="Map" resultType="com.rest.api.entity.User">
/* UserSqlMapper - select */
SELECT #{userId} as MSRL
, #{password} as PASSWORD
, 'test@test.com' as UID
, '홍길동' as USERNAME
, 'ROLE_USER' as ROLES
</select>
</mapper>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- <setting name="cacheEnabled" value="true"/> default value -->
<!-- <setting name="cacheEnabled" value="false"/> -->
<setting name="lazyLoadingEnabled" value="false"/>
<!-- <setting name="multipleResultSetsEnabled" value="true"/> default value -->
<setting name="useColumnLabel" value="true"/> <!-- default value-->
<!-- <setting name="useGeneratedKeys" value="false"/> default value -->
<!-- <setting name="defaultExecutorType" value="SIMPLE"/> default value -->
<setting name="defaultStatementTimeout" value="25000"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="localCacheScope" value="STATEMENT"/>
<setting name="jdbcTypeForNull" value="CHAR"/>
</settings>
</configuration>
application.yml
spring:
profiles:
active: local # 디폴트 환경
jwt:
secret: govlepel@$&
messages:
basename: i18n/exception
encoding: UTF-8
# DB 설정
mssql:
datasource:
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
jdbc-url: jdbc:log4jdbc:sqlserver://localhost:1433;databaseName=SampleRest
username: SAMPLE
password: SAMPLE
auto-commit: false
connection-test-query: SELECT 1
minimum-idle: 3
maximum-pool-size: 10
pool-name: pool-mssql
# mapper.xml 위치 지정
# **은 하위 폴더 레벨에 상관없이 모든 경로를 뜻하며, *는 아무 이름이나 와도 된다는것을 뜻합니다.
mapper-locations: classpath:/mybatis/mapper/mssql/**/*.xml
# mapper.xml에서 resultType을 지정할 때 com.god.bo.test.vo.TestVo 대신 TestVo로 간략히 할 수 있다.
#type-aliases-package: com.god.bo.test.vo
mybatis-config: classpath:/mybatis/mybatis-config.xml
mybatis:
configuration:
map-underscore-to-camel-case: true
application-local.yml
server:
port: 8090 # 서버 포트 변경
logging:
level:
root: warn
spring:
profiles: local
log4jdbc.log4j2.properties
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
#Disable - Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
log4jdbc.auto.load.popular.drivers = false
log4j2-spring.yml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout
pattern="%style{%d{ISO8601}}{black} %highlight{%-5level }[%style{%t}{bright,blue}] %style{%C{1.}}{bright,yellow}: %msg%n%throwable" />
</Console>
<RollingFile name="RollingFile"
fileName="./logs/spring-boot-sample.log"
filePattern="./logs/spring-boot-sample-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout>
<pattern>%d %p %C{1.} [%t] %m%n</pattern>
</PatternLayout>
<Policies>
<!-- rollover on startup, daily and when the file reaches
10 MegaBytes -->
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy
size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="jdbc.sqltiming" level="INFO" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Logger>
<Logger name="jdbc.sqlonly" level="INFO" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Logger>
<!-- log4jdbc logging -->
<Logger name="jdbc" level="error" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Logger>
<Logger name="log4jdbc.log4j2" level="error" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Logger>
<Logger name="com.rest" level="debug" additivity="false">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Logger>
<!-- LOG everything at INFO level -->
<Root level="info">
<AppenderRef ref="console" />
<AppenderRef ref="RollingFile" />
</Root>
</Loggers>
</Configuration>
테스트
728x90
'Spring Boot Framework' 카테고리의 다른 글
[Spring Boot] Spring Resttemplate Sample (0) | 2021.01.31 |
---|---|
[Spring Boot] JUnit Test (0) | 2021.01.17 |
[Spring Boot] Spring Boot + JWT ( JSON Web Token ) 테스트 (0) | 2021.01.10 |
[Spring Boot] Spring Boot Sample REST + Log4J2 (0) | 2021.01.09 |
[Spring Boot] Spring Boot Sample REST (0) | 2021.01.08 |