# 권한 부여하기
---
## 권리자 권한 부여
먼저 관리자 계정을 만들어 줍니다. SQL 쿼리문을 통해 직접 입력 해주세요.
```sql
INSERT INTO authorities (email, authority)
VALUES ('
[email protected]', 'ROLE_ADMIN')
```
권한 부여 후 테이블 조회 결과는 아래와 같습니다.
![Imgur](http://i.imgur.com/18m49Wc.png)
## 관리자 페이지 버튼 등록
해더에 관리 버튼을 만들어 줍시다.
![Imgur](http://i.imgur.com/96cPtvC.png)
아래는 관리 버튼을 추가한 소스코드입니다. 기존 도서 버튼 다음에 관리자 추가해 주시면 됩니다.
**tiles/components/header.jsp**
```jsp
...
<ul class="nav navbar-nav navbar-left">
<li><a href="<c:url value='/books'/>">도서</a></li>
<!-- 관리자 페이지 버튼 -->
<sec:authorize access="hasRole('ADMIN')">
<li class="nav-item"><a class="nav-link" href="<c:url value='/admins'/>">관리</a></li>
</sec:authorize>
</ul>
...
```
## 관리자 페이지 생성
그럼 관리자 페이지를 만들어 줍시다.
**views/admins/index.jsp**
```
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="s"%>
<%@ page pageEncoding="utf-8" %>
<div class="jumbotron">
<h1>관리자 페이지</h1>
<p>views/admins/index.jsp</p>
</div>
```
컨트롤러를 만들어 줍니다.
![Imgur](http://i.imgur.com/F9sUUvq.png)
**AdminsController.java**
```
@Controller
public class AdminsController {
@RequestMapping(value = "/admins", method = RequestMethod.GET)
public String index() {
return "admins/index";
}
}
```
버튼 클릭시 페이지를 볼 수 있게 되었습니다. 클릭하면 아래와 같은 페이지를 볼 수 있습니다.
![Imgur](http://i.imgur.com/zNM136Y.png)
## 사용자 목록 생성
관리자 페이지에 사용자 목록 만들어 줍시다. 반복문을 통해 사용자를 출력해주세요.
**views/admins/index.jsp**
```jsp
...
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>User</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${ users }">
<tr>
<td>${ user.email }</td>
<td>
<c:forEach var="authority" items="${ user.authorities }">${ authority.authority }</c:forEach>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
...
```
컨트롤러에서 사용자리스트를 모델에 등록하지 않았으므로 아직 데이터가 보여지지 않습니다.
![Imgur](http://i.imgur.com/QREvAZO.png)
## 사용자 데이터 가져오기
이제 컨트롤러에서 사용자들의 데이터를 가져와야 합니다.
**AdminsController.java**
```
@Controller
public class AdminsController {
@Autowired
private UserMapper userMapper;
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String index(Model model) {
List<User> users = userMapper.selectUsers();
model.addAttribute("users", users);
return "admins/index";
}
}
```
유저 맵퍼에 selectUsers() 메소드를 만들어줘야 동작을 하겠지요?
**UserMapper.java**
```
...
@Select("SELECT id, email FROM users")
@Results(value = {
@Result(property = "id", column = "id"),
@Result(property = "email", column = "email"),
@Result(property = "authorities", column = "email", javaType = List.class, many = @Many(select = "selectAuthority"))
})
public List<User> selectUsers();
@Select("SELECT email, authority "
+ "FROM authorities "
+ "WHERE email = #{email}")
public List<Authority> selectAuthority(String email);
...
```
위 메소드는 유저 테이블 뿐만 아니라 해당 각 유저의 권한들도 가져오는 코드입니다. 해당 메소드의 정상 동작을 위해서 `User` 클래스를 아래와 같이 변경해 줍니다.
**User.java**
```
public class User {
int id;
// @Email
String email;
// @Size(min = 4, max = 20)
String password;
List<Authority> authorities;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Authority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Authority> authorities) {
this.authorities = authorities;
}
@Override
public String toString() {
return "User [id=" + id + ", email=" + email + ", password=" + password + ", authorities=" + authorities + "]";
}
}
```
위 코드를 보면 `User` 클래스가 Authority 클래스의 리스트를 필드로 가지고 있습니다. Authority 클래스를 생성해야겠지요?
**Authority.java**
```
public class Authority {
String email;
String authority;
public boolean isRole(String role) {
return authority.equals("ROLE_" + role.toUpperCase());
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
@Override
public String toString() {
return "Role [email=" + email + ", authority=" + authority + "]";
}
}
```
자 이제 관리자 페이지를 확인해봅시다!
![Imgur](http://i.imgur.com/U9B7CbM.png)
## 권한 부여 및 삭제
권한 부여 및 삭제 버튼을 추가해줍니다.
**views/admins/index.jsp**
```jsp
...
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>User</th>
<th>Roles</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${ users }">
<tr>
<td>${ user.email }</td>
<td><c:forEach var="authority" items="${ user.authorities }"> ${ authority.authority }, </c:forEach>
</td>
<td><c:url var="changeRoleUrl" value="/admins/role/${ user.id }" />
<a href="${ changeRoleUrl }/admin" class="btn btn-light">관리자</a>
<a href="${ changeRoleUrl }/manager" class="btn btn-light">매니저</a>
<a href="${ changeRoleUrl }/user" class="btn btn-light">사용자</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
...
```
아래와 같은 화면이 나타납니다.
![Imgur](http://i.imgur.com/zV8AVOR.png)
## 권한 부여 기능
버튼을 클릭하여 권한을 부여 또는 삭제 할 수 있게 해줍시다. 버튼 클릭 시 전송되는 요청을 받을 수 있게 컨트롤러에 메소드를 추가해 줍니다.
**AdminsController.java**
```java
@Controller
public class AdminsController {
@Autowired
private UserMapper userMapper;
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String index(Model model) {
List<User> users = userMapper.selectUsers();
model.addAttribute("users", users);
return "admins/index";
}
@RequestMapping(value = "/admin/role/{userId}/{role}")
public String changeRole(@PathVariable int userId, @PathVariable String role) {
toggleRole(userId, role);
return "redirect:/admin";
}
}
```
toggleRole() 메소드를 추가해 줍니다.
**AdminsController.java**
```java
@Controller
public class AdminsController {
@Autowired
private UserMapper userMapper;
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String index(Model model) {
List<User> users = userMapper.selectUsers();
model.addAttribute("users", users);
return "admins/index";
}
@RequestMapping(value = "/admin/role/{userId}/{role}")
public String changeRole(@PathVariable int userId, @PathVariable String role) {
toggleRole(userId, role);
return "redirect:/admin";
}
private void toggleRole(int userId, String role) {
User user = userMapper.selectUserById(userId);
if (! user.hasRole(role))
userMapper.insertAuthority(user.getEmail(), "ROLE_" + role.toUpperCase());
else
userMapper.deleteAuthority(user.getEmail(), "ROLE_" + role.toUpperCase());
}
}
```
마지막으로 `UserMapper` 인터페이스에 아래의 메소드를 추가해줍니다.
+ selectUserById(int);
+ insertAuthority(String, String);
+ deleteAuthority(String, String);
**UserMapper.java**
```java
...
@Select("SELECT id, email FROM users WHERE id = #{userId}")
@Results(value = {
@Result(property = "id", column = "id"),
@Result(property = "email", column = "email"),
@Result(property = "authorities", column = "email", javaType = List.class, many = @Many(select = "selectAuthority"))
})
public User selectUserById(int userId);
@Insert("INSERT INTO authorities (email, authority) "
+ "VALUES (#{email}, #{authority})")
public boolean insertAuthority(@Param("email") String email, @Param("authority") String authority);
@Delete("DELETE FROM authorities WHERE email = #{email} AND authority = #{role}")
public void deleteAuthority(@Param("email") String email, @Param("role") String role);
...
```
이제 버튼을 클릭하면 권한이 부여/삭제가 됩니다.
## 권한에 따른 버튼색 달리하기
좀더 직관적인 UI를 만들어 봅시다. 해당 권한이 있는 경우, 버튼색을 파랗게 해주고 없는경우는 회색으로 만들어 볼까요?
**views/admins/index.jsp**
```jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="s"%>
<%@ page pageEncoding="utf-8"%>
<div class="jumbotron">
<h1>관리자 페이지</h1>
<p>views/admins/index.jsp</p>
</div>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>User</th>
<th>Roles</th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${ users }">
<tr>
<td>${ user.email }</td>
<td><c:forEach var="authority" items="${ user.authorities }"> ${ authority.authority }, </c:forEach>
</td>
<td><c:url var="changeRoleUrl" value="/admins/role/${ user.id }" />
<a href="${ changeRoleUrl }/admin" class="btn <c:if test="${ user.hasRole('ADMIN') }">btn-primary</c:if>">관리자</a>
<a href="${ changeRoleUrl }/manager" class="btn <c:if test="${ user.hasRole('MANAGER') }">btn-primary</c:if>">매니저</a>
<a href="${ changeRoleUrl }/user" class="btn <c:if test="${ user.hasRole('USER') }">btn-primary</c:if>">사용자</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
```
Line 30~32를 보면, 해당 권한에 따른 버튼색을 달리하는 코드가 추가 됨을 알 수 있습니다. 하지만 아직 제대로 동자 하지 안습니다. 왜냐하면 `hasRole` 메소드가 구현되지 않았기 때문입니다. 정상 동작을 위해 `User` 클래스에 hasRole(String) 메소드를 구현해줍니다.
**User.java**
```java
public class User {
int id;
// @Email
String email;
// @Size(min = 4, max = 20)
String password;
List<Authority> authorities;
public boolean hasRole(String role) {
for (Authority auth : authorities)
if (auth.isRole(role))
return true;
return false;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Authority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Authority> authorities) {
this.authorities = authorities;
}
@Override
public String toString() {
return "User [id=" + id + ", email=" + email + ", password=" + password + ", authorities=" + authorities + "]";
}
}
```
위 코드가 동작하기 위해서는 `Authority` 클래스에도 isRole(String) 메소드를 만들어 줘야합니다.
**Authority.java**
```java
public class Authority {
String email;
String authority;
public boolean isRole(String role) {
return authority.equals("ROLE_" + role.toUpperCase());
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
@Override
public String toString() {
return "Role [email=" + email + ", authority=" + authority + "]";
}
}
```
모든 준비가 끝났습니다. 관리자 페이지를 확인해봅시다.
![Imgur](http://i.imgur.com/CQW0p32.png)