- 인증 (Authentication) : 사용자의 신원을 검증, 패스워드 입력, 로그인
- 인가 (Authorization) : 사용자에게 특정 리소스나 기능에 액세스할 수 있는 권한 부여
- 접근 주체 (Principal) : 보호된 리소스에 접근하는 주체
사용자가 서버에 접속할 때, 리퀘스트를 가로챈다. (인증 관리자, 접근 권한 관리자)
구현 방법 : Filter 기반 동작, 쿠키와 세션 방식으로 인증
HomeController.java (유저, 어드민에 대한 함수 추가)
@GetMapping("/user/userHome")
public void user_home() {
log.info("userHome ...");
}
@GetMapping("/admin/adminHome")
public void admin_home() {
log.info("adminHome ... ");
}
SecurityConfig.java (configure 수정 - 권한 부여)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // CSRF 설정 해제 (초기 개발시 꺼놓고 작업)
http.authorizeRequests()
.antMatchers("/user/**").hasAnyRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN");
http.formLogin();
}
SecurityConfig.java (내용 변경)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // CSRF 설정 해제 (초기 개발시 꺼놓고 작업)
http.authorizeRequests()
.antMatchers("/user/**").hasAnyRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/**").permitAll();
http.formLogin();
}
시큐리티 설명 파일 추가
SQL 파일을 Developers에 내용 넣고 실행
create table users(
username varchar2(50) not null primary key,
password varchar2(100) not null,
enabled char(1) DEFAULT '1'
);
create table authorities (
username varchar2(50) not null,
authority varchar2(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
commit;
insert into users (username,password) values('user','user');
insert into users (username,password) values('member','member');
insert into users (username,password) values('admin','admin');
commit;
insert into AUTHORITIES (username,AUTHORITY) values('user','ROLE_USER');
insert into AUTHORITIES (username,AUTHORITY) values('member','ROLE_MANAGER');
insert into AUTHORITIES (username,AUTHORITY) values('admin','ROLE_MANAGER');
insert into AUTHORITIES (username,AUTHORITY) values('admin','ROLE_ADMIN');
commit;
UserVO.java
package edu.global.ex.vo;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserVO {
private String username;
private String password;
private int enabled;
private List<AuthVO> authList;
}
AuthVO.java
package edu.global.ex.vo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class AuthVO {
private String username;
private String authority;
}
# MyBatis
#xml location
mybatis.mapper-locations=classpath:mappers/**/*.xml
mybatis.type-aliases-package=edu.global.ex
UserMapper.java
package edu.global.ex.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import edu.global.ex.vo.UserVO;
@Mapper
public interface UserMapper {
public UserVO getUser(String username);
@Insert("insert into users(username,password,enabled) values(#{username},#{password},#{enabled})")
public int insertUser(UserVO userVO);
@Insert("insert into AUTHORITIES (username,AUTHORITY) values(#{username},'ROLE_USER')")
public void insertAuthorities(UserVO UserVO);
}
UserMapper.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="edu.global.ex.mapper.UserMapper">
<resultMap id="userMap" type="UserVO">
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="enabled" column="enabled"/>
<collection property="authList" resultMap="authMap"></collection>
</resultMap>
<resultMap id="authMap" type="AuthVO">
<result property="username" column="username"/>
<result property="authority" column="authority"/>
</resultMap>
<select id="getUser" resultMap="userMap">
select * from users , authorities
where users.username = authorities.username and users.username = #{username}
</select>
</mapper>
UserDetailsVO.java
package edu.global.ex.vo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class UserDetailsVO implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> authorities;
public UserDetailsVO(UserVO user) {
this.setAuthorities(user);
this.setPassword(user.getPassword());
this.setUsername(user.getUsername());
}
// setter
public void setUsername(String username) {
this.username = username;
}
// setter
public void setPassword(String password) {
this.password = password;
}
// setter
public void setAuthorities(UserVO userVO) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (AuthVO auth : userVO.getAuthList()) {
authorities.add(new SimpleGrantedAuthority(auth.getAuthority()));
}
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return authorities;
}
@Override
public String getUsername() {
// TODO Auto-generated method stub
return username;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
@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 true;
}
}
CustomUserDetailsService.java
package edu.global.ex.security;
import org.springframework.beans.factory.annotation.Autowired;
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;
import edu.global.ex.mapper.UserMapper;
import edu.global.ex.vo.UserDetailsVO;
import edu.global.ex.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.warn("Load User By UserVO number: " + username);
UserVO vo = userMapper.getUser(username);
log.warn("queried by UserVO mapper: " + vo);
return vo == null ? null : new UserDetailsVO(vo);
}
}
SecurityConfig.java (클래스 안에 autowired 추가, configure 내용 수정)
package edu.global.ex.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import edu.global.ex.security.CustomUserDetailsService;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/user/**").hasAnyRole("USER").antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/**").permitAll();
http.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
2023-04-18 12:36:42.812 WARN 10172 --- [nio-8282-exec-5] e.g.e.security.CustomUserDetailsService : Load User By UserVO number: user
2023-04-18 12:36:42.814 WARN 10172 --- [nio-8282-exec-5] e.g.e.security.CustomUserDetailsService : queried by UserVO mapper: UserVO(username=user, password=user, enabled=1, authList=[AuthVO(username=user, authority=ROLE_USER)])
2023-04-18 12:36:42.815 WARN 10172 --- [nio-8282-exec-5] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
UserMapper.java
testInsertUser 부분을 오른쪽 버튼 누르고 Run As - JUnit Test
Developer에서 새로운 계정을 확인한다.
localhost:8282/user/userHome 접속하고 kim2, kim2 접속
2023-04-18 12:53:57.828 WARN 3184 --- [nio-8282-exec-9] e.g.e.security.CustomUserDetailsService : Load User By UserVO number: kim2
2023-04-18 12:53:57.832 WARN 3184 --- [nio-8282-exec-9] e.g.e.security.CustomUserDetailsService : queried by UserVO mapper: UserVO(username=kim2, password=$2a$10$.OvP8tpGwQPZQNHr63aMyu8eXGE7pM0qqUZfNrNNlKJsdm0DqZZTe, enabled=1, authList=[AuthVO(username=kim2, authority=ROLE_USER)])
2023-04-18 12:53:57.955 INFO 3184 --- [io-8282-exec-10] edu.global.ex.controller.HomeController : userHome ...