Table생성
CREATE TABLE Member
(
userid VARCHAR(20) PRIMARY KEY,
passwd VARCHAR(100) NOT NULL,
name VARCHAR(20) NOT NULL,
role VARCHAR(45) NOT NULL,
enabled TINYINT DEFAULT 1
);
username이 아닌 name이라고 지정한 이유는 Security에서 username은 아이디로 인식하기 때문이다.
test 데이터 삽입
INSERT INTO Member VALUES('member', '12345678', '회원', 'ROLE_MEMBER', 1);
INSERT INTO Member VALUES('manager', '12345678', '매니저', 'ROLE_MANAGER', 1);
INSERT INTO Member VALUES('admin', '12345678', '어드민', 'ROLE_ADMIN', 1);
COMMIT;
권한을 부여할 떄 ROLE_를 적어야한다.
ApplicationConfig.properties 수정
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=
spring.datasource.password=
pom.xml수정
Spring Configuration Processor, JDBC API, MySQL Driver을 추가해준다.
SecurityConfig.java
@EnableWebSecurity
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MemberService memberService;
@Override
protected void configure(HttpSecurity http) throws Exception{
log.info("security config...");
http.authorizeRequests().antMatchers("/").permitAll();
http.authorizeRequests().antMatchers("/member/**").authenticated();
http.authorizeRequests().antMatchers("/manager/**").hasRole("MANAGER");
http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN");
http.formLogin().loginPage("/login").defaultSuccessUrl("/loginSuccess", true);
http.exceptionHandling().accessDeniedPage("/accessDenied");
http.logout().logoutUrl("/logout").invalidateHttpSession(true).logoutSuccessUrl("/login");
http.userDetailsService(memberService); // DB에서 정보를 받아 인증처리를 해준다
}
}
MemberVO.java생성
@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class MemberVO {
private String userid;
private String passwd;
private String name;
private String role;
private int enabled;
}
MyUserService.java생성
@Service
@Slf4j
public class MemberService implements UserDetailsService{
@Autowired
private MemberDao memberDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("called loadUserByUsername() user id {}", username);
MemberVO member = this.memberDao.getUserByID(username);
log.debug("memer = {}", member.toString());
Collection<SimpleGrantedAuthority> roles = new
ArrayList<SimpleGrantedAuthority>();
roles.add(new SimpleGrantedAuthority(member.getRole()));
log.debug("Role : {}", roles.toString());
UserDetails user = new User(username, member.getPasswd(), roles);
return user;
}
}
UserDetailService를 상속받으며 userDetails method를 사용하여 유저의 정보를 관리한다.
ArrayList를 사용한 이유는 한 유저가 여러개의 권한을 가질 수 있기 때문이다.
SampleGrantedAuthority라는 객체를 이용해 권한을 가져온다.
UserDetails라는 인터페이스에게 만들어진 유저를 생성해서 넘겨준다.
Security가 로그인한 계정관리를 한다.
MemberDao.java생성
package com.example.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import com.example.vo.MemberVO;
@Repository("memberDao")
public class MemberDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public int insertMember(MemberVO member) {
String sql = "INSERT INTO Member(userid, passwd, name, role, enabled) VALUES(?,?,?,?,?)";
return this.jdbcTemplate.update(sql, member.getUserid(), member.getPasswd(),
member.getName(), member.getRole(), member.getEnabled());
}
public MemberVO getUserByID(String username) {
String sql = "SELECT userid, passwd, name, role, enabled FROM Member WHERE userid=?";
return this.jdbcTemplate.queryForObject(sql, new RowMapper<MemberVO>() {
@Override
public MemberVO mapRow(ResultSet rs, int rowNum) throws SQLException {
MemberVO member = new MemberVO();
member.setUserid(rs.getString("userid"));
member.setPasswd(rs.getString("passwd"));
member.setName(rs.getString("name"));
member.setRole(rs.getString("role"));
member.setEnabled(rs.getInt("enabled"));
return member;
}
},new String[] {username});
}
}
Password앞에 {noop}을 빼야 읽어올 때 제대로 읽어올 수 있다.
PasswordEncoder
SecurityConfig.java수정
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Password Encoder을 interface가 암호화의 기능을 제공해준다.
BCrtptPasswordEncoder를 사용하며 BCrtpt라는 알고리즘을 사용한다.
PasswordEncoderTest.java(JupiterTest)생성
@SpringBootTest
@ExtendWith(SpringExtension.class)
class PasswordEncoderTest {
@Autowired
private MemberDao memberDao;
@Autowired
private PasswordEncoder encoder;
@Test
void test1() {
MemberVO member = new MemberVO();
member.setUserid("member");
member.setPasswd(encoder.encode("12345678"));
member.setName("정회원");
member.setRole("ROLE_MEMBER");
member.setEnabled(1);
int row = this.memberDao.insertMember(member);
assertEquals(1, row);
}
}
Jupiter를 이용하여 데이터를 넣은 후 DB에서 확인을 해보면 위의 사진처럼 password가 암호화되어있는 것을 확인 할 수 있다.
간단하게 사용하는방법
Repository, service를 거치지 않고 바로 처리하는 방법이 있다. 간단하지만 별로 권장하지는 않는 방법이다.
SecurityConfig.java수정
@Override
protected void configure(AuthenticationManagerBuilder auth) throws
java.lang.Exception{
String query1 = "SELECT userid username, passwd password, enabled FROM Member WHERE userid = ?";
String query2 = "SELECT userid, role FROM Member WHERE userid = ?";
auth.jdbcAuthentication().dataSource(dataSource).usersByUsernameQuery(query1).
authoritiesByUsernameQuery(query2);
}
query1은 인증 quety2는 인가에 관련된 내용이다.
thymeleaf에서 Security 적용하기
pom.xml수정
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
dependency를 추가한다.
loginSuccess.html수정
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3><span style="color:red">로그인 인증 성공</span></h3>
<h5><a th:href="@{/}">Index Page로 이동</a></h5>
<h5 sec:authorize="isAuthenticated"><a th:href="@{/member}">Member Page로 이동</a></h5>
<h5 sec:authorize="hasRole('ROLE_MANAGER')"><a th:href="@{/manager}">Manager 전용 Page로 이동</a></h5>
<h5 sec:authorize="hasRole('ROLE_ADMIN')"><a th:href="@{/admin}">Admin 전용 Page로 이동</a></h5>
</body>
</html>
접두사 sec를 이용해서 thymeleaf의 security기능을 이용할 수 있다.
admin.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Admin page : Admin 권한을 가진 분만 보입니다.</h1>
<ul>
<li>User name : <span sec:authentication="name"></span></li>
<li>Authorities : <span sec:authentication="principal.authorities"></span></li>
</ul>
<a th:href="@{/loginSuccess}">뒤로 가기</a>
<form th:action="@{/logout}" method="get">
<button sec:authorize="isAuthenticated()">Logout</button>
<button sec:authorize="isAnonymous()" th:href="@{/login}">Login</button>
</form>
</body>
</html>
principal.authrities를 통해 어떤 권한을 가지고 있는지 확인할 수 있다.
'SpringBoot 코딩' 카테고리의 다른 글
12. Naver, Google을 이용하여 oauth인증하기 (0) | 2021.12.12 |
---|---|
11. Thymeleaf의 sec nameSpace가 없을 때 사용하는 방법 (SpringBootSecurity Demo) (0) | 2021.12.12 |
9. Spring SeCurity (SpringBootSecurityDemo) (0) | 2021.12.11 |
8. RestFulApi를 이용한 프로젝트만들기, war packaging 진짜 Tomcat사용하기 (demo) (0) | 2021.12.09 |
7.2 Mybatis와 hikariCP를 이용한 프로젝트 만들기(BootJdbcDemo) (0) | 2021.11.26 |