해당소스는 https://cusonar.tistory.com/8?category=607756 사이트를 참고했습니다.
1. Application structure
2. DB생성
CREATE TABLE `authority` (
`username` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
`authority_name` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
PRIMARY KEY (`username`,`authority_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `authority` VALUES ('abc','USER');
CREATE TABLE `user` (
`username` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL,
`name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,
`isAccountNonExpired` tinyint(1) DEFAULT NULL,
`isAccountNonLocked` tinyint(1) DEFAULT NULL,
`isCredentialsNonExpired` tinyint(1) DEFAULT NULL,
`isEnabled` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
시작전에 테이블을 생성해주세요.
authority테이블을 따로 나눈이유는 ADMIN과 USER권한을 모두 가진 유저도 존재할 수 있기 때문입니다.
3. properties 설정 및 Mysql 연동
# mysql 연동
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db테이블명?useUniCode=yes&characterEncoding=UTF-8&serverTimezone=Asia/Seoul
spring.datasource.username=username
spring.datasource.password=password
# xml위치설정 및 별칭사용
mybatis.type-aliases-package=com.security_blog.yg1110.domain
mybatis.mapper-locations=mapper/**/*.xml
4. User Domain 생성
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails {
private static final long serialVersionUID = 5177294317961485740L;
private String username;
private String password;
private String name;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
private Collection<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
return isEnabled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setAccountNonExpired(boolean isAccountNonExpired) {
this.isAccountNonExpired = isAccountNonExpired;
}
public void setAccountNonLocked(boolean isAccountNonLocked) {
this.isAccountNonLocked = isAccountNonLocked;
}
public void setCredentialsNonExpired(boolean isCredentialsNonExpired) {
this.isCredentialsNonExpired = isCredentialsNonExpired;
}
public void setEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password + ", name=" + name + ", isAccountNonExpired="
+ isAccountNonExpired + ", isAccountNonLocked=" + isAccountNonLocked + ", isCredentialsNonExpired="
+ isCredentialsNonExpired + ", isEnabled=" + isEnabled + "]";
}
public User(String username, String password, String name, boolean isAccountNonExpired, boolean isAccountNonLocked,
boolean isCredentialsNonExpired, boolean isEnabled) {
super();
this.username = username;
this.password = password;
this.name = name;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
}
username부터 isEnabled는 유저에 사용되는 정보입니다.
authorities의 경우 ADMIN또는 USER를 리스트에 담아 authority의 테이블에 추가할때 사용하게됩니다.
5. UserMapper 생성
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.security_blog.yg1110.domain.User;
@Mapper
public interface UserMapper {
public List<User> Userlist();
public User readUser(String username);
public List<String> readAuthority(String username);
public void createUser(User user);
public void createAuthority(User user);
public void deleteUser(String username);
public void deleteAuthority(String username);
}
6. user.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="com.security_blog.yg1110.mapper.UserMapper">
<select id="Userlist" resultType="User">
SELECT * FROM user
</select>
<select id="readUser" parameterType="String" resultType="User">
SELECT * FROM user WHERE username = #{username}
</select>
<select id="readAuthority" parameterType="String" resultType="String">
SELECT authority_name FROM authority WHERE username = #{username}
</select>
<insert id="createUser" parameterType="User">
INSERT INTO user (username, password, name, isAccountNonExpired,
isAccountNonLocked, isCredentialsNonExpired, isEnabled)
VALUES (#{username}, #{password}, #{name}, #{isAccountNonExpired},
#{isAccountNonLocked}, #{isCredentialsNonExpired}, #{isEnabled})
</insert>
<insert id="createAuthority"
parameterType="org.springframework.security.core.GrantedAuthority">
INSERT INTO authority (username, authority_name)
VALUES
<foreach item="item" collection="authorities" separator=",">
(#{username}, #{item.authority})
</foreach>
</insert>
<delete id="deleteUser" parameterType="String">
DELETE FROM user WHERE username = #{username}
</delete>
<delete id="deleteAuthority" parameterType="String">
DELETE FROM authority WHERE username = #{username}
</delete>
</mapper>
properties에서 설정했던 경로 아래에 xml파일을 만들고 다음과 같이 쿼리를 작성합니다.
createAuthority의 경우 유저에게 입력받은 권한 리스트를 foreach를 돌려 동적으로 DB에 저장하고 있습니다.
6. UserService 생성
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.security_blog.yg1110.domain.User;
public interface UserService extends UserDetailsService {
Collection<GrantedAuthority> getAuthorities(String username);
public void createUser(User user);
public void deleteUser(String username);
public PasswordEncoder passwordEncoder();
}
7. UserServiceImpl 생성
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.security_blog.yg1110.domain.User;
import com.security_blog.yg1110.mapper.UserMapper;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.readUser(username);
if(user==null) {
throw new UsernameNotFoundException(username);
}
user.setAuthorities(getAuthorities(username));
return user;
}
public Collection<GrantedAuthority> getAuthorities(String username) {
List<String> string_authorities = userMapper.readAuthority(username);
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String authority : string_authorities) {
authorities.add(new SimpleGrantedAuthority(authority));
}
return authorities;
}
@Override
public void createUser(User user) {
String rawPassword = user.getPassword();
String encodedPassword = new BCryptPasswordEncoder().encode(rawPassword);
user.setPassword(encodedPassword);
userMapper.createUser(user);
userMapper.createAuthority(user);
}
@Override
public void deleteUser(String username) {
userMapper.deleteUser(username);
userMapper.deleteAuthority(username);
}
@Override
public PasswordEncoder passwordEncoder() {
return this.passwordEncoder;
}
}
1) loadUserByUsername
유저가 아이디와 패스워드를 입력하면 loadUserByUsername 메소드가 실행되게 됩니다.
사용자가 입력한 username의 정보를 db에서 검색하고, 없을경우 에러를 발생시키고
찾았을 경우 getAuthorities메소드를 실행하게 됩니다.
2) getAuthorities
authority테이블에서 유저가 입력한 아이디로 권한을 검색하게 됩니다.
그 권한정보를 List<GrantedAuthority>에 담고 리턴시킵니다.
3) createUser
사용자 추가 메소드입니다.
시큐리티의 경우 암호화가 되어있지 않으면 실행되지 않기때문에 BCryptPasswordEncoder를 이용하여
암호화를 진행하여 user테이블에 저장하고 authority테이블에 권한도 저장합니다.
4) deleteUser
사용자 삭제 메소드입니다.
user테이블의 정보와 authority테이블의 정보를 삭제합니다.
8. SecurityConfig 생성
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.WebSecurityConfigurerAdapter;
import com.security_blog.yg1110.servicer.UserService;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// 루트와 createuser요청에 대해서는 요청을 허용한다.
.antMatchers("/", "/createuser").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(userService.passwordEncoder());
}
}
로그인 진행시 위에서 만든 UserService와 암호화를 사용하여 로그인을 수행합니다.
9. UserController 생성
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.security_blog.yg1110.domain.User;
import com.security_blog.yg1110.mapper.UserMapper;
import com.security_blog.yg1110.servicer.UserServiceImpl;
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
@Autowired UserServiceImpl userservice;
@RequestMapping("/")
public List<User> user_list() {
return userMapper.Userlist();
}
@RequestMapping("/user_auth/{username}")
public List<String> user_auth(@PathVariable String username) {
return userMapper.readAuthority(username);
}
@RequestMapping("/createuser")
public void createuser() {
User user = new User("a", "a", "a", true, true, true, true);
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ADMIN"));
authorities.add(new SimpleGrantedAuthority("USER"));
user.setAuthorities(authorities);
userservice.createUser(user);
}
@RequestMapping("/deleteuser/{username}")
public void deleteuser(@PathVariable String username) {
userservice.deleteUser(username);
}
}
컨트롤러 부분입니다.
간단하게 유저리스트, 유저권한정보, 유저생성, 유저삭제를 만들어보았습니다.
1) 유저생성
먼저 유저가 없기때문에 유저를 생성해보겠습니다.
화면단을 만들지 않았기 때문에 user 객체에 a를 넣고 권한리스트에는 ADMIN과 USER를 넣고 가입을 진행해보겠습니다.
http://localhost:8080/createuser 를 호출하게 되면
다음과 같이 디비에 저장되게됩니다.
2) 유저권한조회
방금 만든 유저정보로 로그인을 하고 http://localhost:8080/user_auth/a 를 호출하게 되면
다음과 같이 a유저의 권한이 나오게 됩니다.
'Springboot' 카테고리의 다른 글
Springboot security (6) - Oauth2.0 (Google, facebook) 적용 (0) | 2020.03.07 |
---|---|
Springboot security (5) - jwt 적용 (0) | 2020.03.03 |
Springboot security (4) - Oauth2.0 (google) 적용 (0) | 2020.02.28 |
Springboot security (3) - 권한설정 (0) | 2020.02.26 |
Springboot security (1) - 기본적인 인증과정 (0) | 2020.02.23 |