# Domain Object Security (ACLs) in Spring
###### tags: `Spring` `security` `acl`
## 名詞解釋
### ACL
為 [Access-Control List](https://en.wikipedia.org/wiki/Access-control_list "存取控制清單") 的縮寫;而 [access control](https://en.wikipedia.org/wiki/Access_control "存取控制") 則:
* 是指**允許**或**禁止**某人使用某項資源的**能力**。
* 包含了 [authentication](#authentication "認證")、[authorization](#authorization "授權") 以及 [audit](#audit "稽核")。
### anonymous
#### Overview
It’s generally considered good security practice to adopt a "deny-by-default" where you explicitly specify what is allowed and disallow everything else. Defining what is accessible to unauthenticated users is a similar situation, particularly for web applications. Many sites require that users must be authenticated for anything other than a few URLs (for example the home and login pages). In this case it is easiest to define access configuration attributes for these specific URLs rather than have for every secured resource. Put differently, sometimes it is nice to say `ROLE_SOMETHING` is required by default and only allow certain exceptions to this rule, such as for login, logout and home pages of an application. You could also omit these pages from the filter chain entirely, thus bypassing the access control checks, but this may be undesirable for other reasons, particularly if the pages behave differently for authenticated users.
This is what we mean by anonymous authentication. Note that there is no real conceptual difference between a user who is "anonymously authenticated" and an unauthenticated user. Spring Security’s anonymous authentication just gives you a more convenient way to configure your access-control attributes. Calls to servlet API calls such as `getCallerPrincipal`, for example, will still return null even though there is actually an anonymous authentication object in the `SecurityContextHolder`.
There are other situations where anonymous authentication is useful, such as when an auditing interceptor queries the `SecurityContextHolder` to identify which principal was responsible for a given operation. Classes can be authored more robustly if they know the `SecurityContextHolder` always contains an `Authentication` object, and never `null`.
#### Configuration
Anonymous authentication support is provided automatically when using the HTTP configuration Spring Security 3.0 and can be customized (or disabled) using the `<anonymous>` element. You don’t need to configure the beans described here unless you are using traditional bean configuration.
Three classes that together provide the anonymous authentication feature. `AnonymousAuthenticationToken` is an implementation of `Authentication`, and stores the `GrantedAuthority` s which apply to the anonymous principal. There is a corresponding `AnonymousAuthenticationProvider`, which is chained into the `ProviderManager` so that `AnonymousAuthenticationToken` s are accepted. Finally, there is an `AnonymousAuthenticationFilter`, which is chained after the normal authentication mechanisms and automatically adds an `AnonymousAuthenticationToken` to the `SecurityContextHolder` if there is no existing `Authentication` held there. The definition of the filter and authentication provider appears as follows:
```
<bean id="anonymousAuthFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="key" value="foobar"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="anonymousAuthenticationProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/>
</bean>
```
The `key` is shared between the filter and authentication provider, so that tokens created by the former are accepted by the latter \[[6](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#_footnote_6 "View footnote.")\]. The `userAttribute` is expressed in the form of `usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]`. This is the same syntax as used after the equals sign for the `userMap` property of `InMemoryDaoImpl`.
As explained earlier, the benefit of anonymous authentication is that all URI patterns can have security applied to them. For example:
```
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadata">
<security:filter-security-metadata-source>
<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/**' access='ROLE_USER'/>
</security:filter-security-metadata-source>" +
</property>
</bean>
```
#### AuthenticationTrustResolver
Rounding out the anonymous authentication discussion is the `AuthenticationTrustResolver` interface, with its corresponding `AuthenticationTrustResolverImpl` implementation. This interface provides an `isAnonymous(Authentication)` method, which allows interested classes to take into account this special type of authentication status. The `ExceptionTranslationFilter` uses this interface in processing `AccessDeniedException` s. If an `AccessDeniedException` is thrown, and the authentication is of an anonymous type, instead of throwing a 403 (forbidden) response, the filter will instead commence the `AuthenticationEntryPoint` so the principal can authenticate properly. This is a necessary distinction, otherwise principals would always be deemed "authenticated" and never be given an opportunity to login via form, basic, digest or some other normal authentication mechanism.
You will often see the `ROLE_ANONYMOUS` attribute in the above interceptor configuration replaced with `IS_AUTHENTICATED_ANONYMOUSLY`, which is effectively the same thing when defining access controls. This is an example of the use of the `AuthenticatedVoter` which we will see in the [authorization chapter](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#authz-authenticated-voter). It uses an `AuthenticationTrustResolver` to process this particular configuration attribute and grant access to anonymous users. The `AuthenticatedVoter` approach is more powerful, since it allows you to differentiate between anonymous, remember-me and fully-authenticated users. If you don’t need this functionality though, then you can stick with `ROLE_ANONYMOUS`, which will be processed by Spring Security’s standard `RoleVoter`.
[Spring 官方說明](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#anonymous)
### audit
* 意思與審核相近,但結果只用**可**或**不可**來表示。
### principal
* 當前登入的使用者 Currently logged in user
* 一個可以被辨識的唯一身分
* 表示對受保護的資源請求的身份,可以是`A user`、`An application`、`A role`,例如:利用FB或是LINE等應用程式登入的這一個"身份or實體"。
* 而 Spring Security 中的 principal 是來自Authentication.getPrincipal(),回傳值為被驗證或已被驗證的主體。在一個帶有使用者名稱(username)及密碼(password)的驗證請求(authentication request)中,principal即為username。
[菜鳥工程師 肉豬](https://matthung0807.blogspot.com/2018/03/spring-security-principal.html)
[維基百科](https://en.wikipedia.org/wiki/Principal_(computer_security))
[java.security.Principal](https://docs.oracle.com/javase/8/docs/api/java/security/Principal.html)
### role
* Group of authorities
* 具有相同權限的多個主體(pricipals)的一個群組
* 分為
* dynamic role
* $everyone (全部使用者)
* $unauthenticated (未驗證的使用者)
* $owner (為該 model 的擁有者主體(principal)), which can be:
* A simple property called userId
* A simple property called owner
* A relation to a model that extends User.
* static role
* admin (一個被定義的管理員角色)
[維基百科](https://en.wikipedia.org/wiki/Role-based_access_control)
[loopback](https://loopback.io/doc/en/lb2/Authentication-authorization-and-permissions.htm)
### SecurityContext
* 保存身份驗證和安全性相關信息
[core-components](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#core-components)
### UserDetails
* 用來儲存用戶的相關資訊
* 登入後會將資訊放入 Authentication 的 SecurityContext 中
[The UserDetailsService](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#tech-userdetailsservice)
[自訂驗證(Customize Authentication)使用UserDetailsService](https://ithelp.ithome.com.tw/articles/10160900)
### GrantedAuthority
* 主要是用來授予主體(principal)權限
* 是只有一種 method 的 interface `String getAuthority();`
* 可以使用`getAuthority`來取得用戶的所有權限
[tech-granted-authority](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#core-components)
### authority
* What they can do?
* 主體的權限,通常是 "roles",例如:`ROLE_ADMINISTRATOR or ROLE_HR_SUPERVISOR`。
* 由`AuthenticationManager `設置
[Spring 官方說明](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#authz-authorities)
[docs.spring.io](https://docs.spring.io/spring-security/site/docs/5.0.x/api/org/springframework/security/core/context/SecurityContextHolder.html)
### authentication
* Who is the user?
* 以特定於 Spring Security 的方法表示(封裝)主體 `Authentication, to represent the principal in a Spring Security-specific manner.`
```
public interface Authentication extends Principal, Serializable {
public Collection<? extends GrantedAuthority> getAuthorities();
public Object getCredentials();
public Object getDetails();
public Object getPrincipal();
public boolean isAuthenticated();
public void setAuthenticated(boolean bln) throws IllegalArgumentException;
}
```
* 一個標準的驗證情景
1. 提示用戶以帳號密碼登入
2. 系統(成功)驗證用戶名的密碼正確
3. 獲取該用戶的資訊 (例如:他們的角色列表)
4. 為用戶建立 security context
5. 接著針對用戶後續的操作,該操作可能會受到訪問控制機制,此時再根據當前的 security context 去檢查該操作所需的權限
* 在 Spring Scurity 中如何運作:
1. 獲取 username 和 password,並將其組合到一個`UsernamePasswordAuthenticationToken`(我們在前面看到的`Authentication` interface 的實例)的實例中。
2. token 會傳遞到`AuthenticationManager`的實例進行驗證。
3. 成功進行身份驗證後,將`AuthenticationManager`返回填充完全的`Authentication`實例。
4. 通過呼叫`SecurityContextHolder.getContext().setAuthentication(…)`並傳入返回的身份驗證對象來建立 security context。
[Spring 官方說明](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#tech-intro-authentication)
[維基百科](https://en.wikipedia.org/wiki/Authentication)
[docs.spring.io](https://docs.spring.io/spring-security/site/docs/5.0.x/api/org/springframework/security/core/context/SecurityContextHolder.html)
### authorization
* Are they allowed to do this? Yes/No
* 授權是指定對資源的訪問權限/特權的功能,通常與資訊安全與電腦安全有關,特別是與訪問控制有關
* 更正式地說,**授權**是定義訪問策略。
[Spring 官方說明](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#authorization)
[維基百科](https://en.wikipedia.org/wiki/Authorization)
### permission
* `PermissionCollection` 的子類別,可用`add()`將 permission object 加到該 permission 所屬的 permissionCollection 中
* 可應用於 method-level (@PostFilter、@PostAuthorize、@PreAuthorize)
* 在默認情況下(未自定義 permission)的情況下,Spring ACL引用 `BasePermission` class 以獲得所有可用權限,提供READ,WRITE,CREATE,DELETE和ADMINISTRATION權限。
```
@PostFilter("hasPermission(filterObject, 'READ')")
List<NoticeMessage> findAll();
@PostAuthorize("hasPermission(returnObject, 'READ')")
NoticeMessage findById(Integer id);
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);
```
[Spring acl簡介](https://www.baeldung.com/spring-security-acl)
[docs.oracle.com](https://docs.oracle.com/javase/8/docs/api/java/security/Permissions.html)
[維基百科](https://en.wikipedia.org/wiki/Application_permissions)
Apple 開發者說明
* [About Authentication, Authorization, and Permissions](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AuthenticationAndAuthorizationGuide/Introduction/Introduction.html)
* [About Software Security](https://developer.apple.com/library/archive/documentation/Security/Conceptual/Security_Overview/Introduction/Introduction.html#//apple_ref/doc/uid/TP30000976)
* [Authentication and Identification In Depth](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AuthenticationAndAuthorizationGuide/Authentication/Authentication.html#//apple_ref/doc/uid/TP40011200-CH4-SW1)
* [Understanding Permissions](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AuthenticationAndAuthorizationGuide/Permissions/Permissions.html#//apple_ref/doc/uid/TP40011200-CH5-SW1)
#### [youtube:youtubeFive Spring Security Concepts - Authentication vs authorization](https://www.youtube.com/watch?v=I0poT4UxFxE)

### user、role、principal 的區別
* user:代表使用者本人
* role:在應用程序中所具備的角色、身份(例如:管理員、講師、會員)(可重複)
* principal:代表請求訪問的身份(例如:一般登入、Facebook 登入、Google 登入)(不可重複)
## Data Access Layer <sub>資料存取層</sub>
### `acl_sid`<sub>權限表</sub>
| Identifier | Data Type | Nullable | Default | Uniqueness | Comment |
| - | - | - | - | - | - |
| `id` | `serial8` | | | `PRIMARY KEY` | 主鍵 |
| `sid` | `varchar` | `NOT NULL` | | | 權限名稱||帳號 |
| `principal` | `boolean` | `NOT NULL` | | | 是否是帳號 |
### `acl_class`<sub>受保護的 class</sub>
| Identifier | Data Type | Nullable | Default | Uniqueness | Comment |
| - | - | - | - | - | - |
| `id` | `serial8` | | | `PRIMARY KEY` | 主鍵 |
| `class` | `varchar` | `NOT NULL` | | | domain object class 的 [FQN](https://en.wikipedia.org/wiki/Fully_qualified_name) |
### `acl_object_identity`<sub>權限身分</sub>
| Identifier | Definition | Comment |
| -------- | -------- | -------- |
| `id` | `serial8 PRIMARY KEY` | 主鍵 |
| `object_id_class` | `int8 NOT NULL` | 持久層 class |
| `object_id_identity` | `varchar NOT NULL` | 持久層 Primary Key |
| `parent_object` | `int8` | 父層 |
| `owner_sid` | `int8` | 外鍵:`acl_sid` |
| `entries_inheriting` | `boolean NOT NULL DEFAULT'0'` | 是否繼承父層權限 |
### `acl_entry`<sub>資訊授權<sub>
| Identifier | Definition | Comment |
| -------- | -------- | -------- |
| `id` | `serial8 PRIMARY KEY` | 主鍵 |
| `acl_object_identity` | `int8 NOT NULL` | acl_object_identity主鍵 |
| `ace_order` | `int4 NOT NULL` | Acl權限順序 |
| `sid` | `int8 NOT NULL` | 外鍵:`acl_sid` |
| `mask` | `int8 NOT NULL` | 權限掩碼(對應到acl_object_identity.owner_sid) |
| `granting` | `BOOLEAN NOT NULL` | 是否授權 |
| `audit_success` | `BOOLEAN NOT NULL` | 是否開啟審核成功訊息 |
| `audit_failure` | `BOOLEAN NOT NULL` | 是否開啟審核失敗訊息 |
## 註釋 <sub>annotation</sub>
### `{package}/AclMethodSecurityConfiguration`
```
@Configuration
@EnableGlobalMethodSecurity(
jsr250Enabled = true, // 是否啟用 @RoleAllowed
prePostEnabled = true, // 是否啟用 @PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter
securedEnabled = true // 是否啟用 @Secured
)
public class AclMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Autowired
MethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler;
@Autowired
DataSource dataSource;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodSecurityExpressionHandler;
}
}
```
## [@PreAuthorize](https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/access/prepost/PreAuthorize.html):先驗證是否可以訪問,在使用調用方法
## [@PostAuthorize](https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/access/prepost/PostAuthorize.html):調用方法後,才驗證是否可以訪問
3. [@Secured](https://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/apidocs/org/springframework/security/access/annotation/Secured.html):使用在服務層,根據權限可以使用的資料表操作(CRUD)
4. [@RoleAllowed](https://docs.oracle.com/javaee/7/api/javax/annotation/security/RolesAllowed.html):與 @Secured 用法相同,差別在於 @RoleAllowed 是 JAVA 標準標籤([JSR 250](https://en.wikipedia.org/wiki/JSR_250)),@Secured 則是 Spring Security 標籤
5. [@PreFilter](https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/access/prepost/PreFilter.html):根據驗證權限過濾此權限可以存取的資料
6. [@PostFilter](https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/access/prepost/PostFilter.html):調用方法後,根據驗證權限過濾此權限可以存取的資料
## [Common Built-In Expressions](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-common-built-in)
| Expression | Description |
| --------------- | ----------- |
|hasRole([role]) | 允許指定的角色,不需要加`ROLE_`前缀。 |
|hasAnyRole([role1, role2])|允許的多個角色(擁有其一即可)。|
|hasAuthority([authority])|允許指定的權限,需要加`ROLE_`前缀。|
|hasAnyAuthority([authority1, authority2])|允許的多個權限(擁有其一即可)。|
|principal|允許直接訪問代表當前用戶的主要對象|
|authentication|允許直接訪問從`SecurityContext`取得的當前認證對象|
|permitAll|允許任何人|
|denyAll|拒絕任何人|
|isAnonymous()|匿名用戶|
|isRememberMe()|"記住我"用戶|
|isAuthenticated()|非匿名用戶|
|isFullyAuthenticated()|非匿名或非"記住我"的用戶|
|hasPermission(Object target, Object permission)|用戶是否有訪問指定目標的指定權限,例如:`hasPermission(domainObject, 'read')`|
|hasPermission(Object targetId, String targetType, Object permission)|用戶是否有訪問指定目標的指定權限,例如: `hasPermission(1, 'com.example.domain.Message', 'read')`
## MutableAclService操作
* createAcl:建立Acl
* insertAce:加入存取權限,有四個參數分別
* 索引值(ACL長度)
* `acl.getEntries().size()`:取得目前ACL長度
* 允許的權限
* `BasePermission`
* `ADMINISTRATION` 使用帳號進行權限存取
* `DELETE` 刪除權限
* `CREATE` 新增權限
* `READ` 讀取權限
* `WRITE` 寫入權限
* 指定權限
* `PrincipalSid()`:設定需要給予的ROLE,需要加入`ROLE_`前缀
* `GrantedAuthoritySid()`: 設定需要給予的USERNAME
* 是否允許使用這個ACL
* Boolean值
* updateAcl
* deleteAcl
## 新增ACL權限
```
/**
* 新增權限
* @param talent
*/
@Transactional
public void addUser(Talent talent) {
ObjectIdentity oid = new ObjectIdentityImpl(Talent.class, talent.getId());
MutableAcl acl = mutableAclService.createAcl(oid);
//insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
acl.insertAce(acl.getEntries().size(), BasePermission.READ,new GrantedAuthoritySid("ROLE_PLATFORM"), true);
mutableAclService.updateAcl(acl);
}
/**
* 刪除權限
*
* @param talent 會員
*/
@Transactional
public void deleteUser(Talent talent) {
ObjectIdentity oid = new ObjectIdentityImpl(Talent.class, talent.getId());
mutableAclService.deleteAcl(oid, false);
}
```
## 參考資料
>[官方說明](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#domain-acls)
>[Spring Security ACL Plugin](https://grails-plugins.github.io/grails-spring-security-acl/v3/index.html)
>[Difference between Role and GrantedAuthority in Spring Security](https://stackoverflow.com/questions/19525380/difference-between-role-and-grantedauthority-in-spring-security)
>[Spring Security中的ACL](https://www.itread01.com/p/1334498.html)
>[Introduction to Spring Security ACL](https://www.baeldung.com/spring-security-acl)
>[Spring Security – Roles and Privileges](https://www.baeldung.com/role-and-privilege-for-spring-security-registration)
>[Spring Security – @PreFilter and @PostFilter](https://www.baeldung.com/spring-security-prefilter-postfilter)
>[Granted Authority Versus Role in Spring Security](https://www.baeldung.com/spring-security-granted-authority-vs-role)
>[spring security中Authority、Role的区别](https://blog.csdn.net/cngkqy/article/details/102919820)
## 協作者
* [高科技黑手](https://hackmd.io/@pclin)
* [歐炫](https://hackmd.io/@OceanChiu)
## 相關筆記
* [Spring Security](https://hackmd.io/@LeeLo/HJvoNobgL)
* [Spring Boot Redis](https://hackmd.io/@LeeLo/HJBTC6Xb8)
* [Spring Security 防止暴力破解身份驗證](https://hackmd.io/@LeeLo/HJyI3yEWL)
* [Internationalization](https://hackmd.io/@LeeLo/By-z7tSZL)
## 延伸閱讀
* [多方授權](https://en.wikipedia.org/wiki/Multi-party_authorization)