一、原有權限系統設計
1.後端API通路控制
在SecurityConfiguartion.java的configure方法中配置
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:")
.and()
.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
.and()
.featurePolicy("geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'")
.and()
.frameOptions()
.deny()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/prometheus").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.and()
.httpBasic()
.and()
.apply(securityConfigurerAdapter());
// @formatter:on
}
URL比對權限有三種:
- permitAll :允許任何人
- authenticated :允許認證的使用者通路
- hasAuthority : 允許指定權限的使用者通路
2.後端URL動詞通路控制
在Restfull接口檔案中,以UserResource.java為例,添加PreAuthorize注釋
/**
* {@code DELETE /users/:login} : delete the "login" User.
*
* @param login the login of the user to delete.
* @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
*/
@DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
@PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Void> deleteUser(@PathVariable String login) {
log.debug("REST request to delete User: {}", login);
userService.deleteUser(login);
return ResponseEntity.noContent().headers(HeaderUtil.createAlert(applicationName, "userManagement.deleted", login)).build();
}
3.後端Entity通路控制
通過 SecurityUtils擷取登入使用者資訊,隻能通路有特定資訊的entity
@GetMapping("/bank-accounts/{id}")
@Timed
public ResponseEntity<BankAccountDTO> getBankAccount(@PathVariable Long id) {
log.debug("REST request to get BankAccount : {}", id);
Optional<BankAccountDTO> bankAccountDTO = bankAccountRepository.findById(id)
.map(bankAccountMapper::toDto);
// Return 404 if the entity is not owned by the connected user
Optional<String> userLogin = SecurityUtils.getCurrentUserLogin();
if (bankAccountDTO.isPresent() &&
userLogin.isPresent() &&
userLogin.get().equals(bankAccountDTO.get().getUserLogin())) {
return ResponseUtil.wrapOrNotFound(bankAccountDTO);
} else {
return ResponseEntity.notFound().build();
}
}
改進:采用注釋的方式
- @PostAuthorize (“returnObject.type == authentication.name”)
DomainUserDetailsService
SecurityUtils:
isCurrentUserInRole
isAuthenticated
getCurrentUserLogin
4.前端module通路控制
Angular每個module都有modulename.route.ts檔案,包含路由資訊
export const bankAccountPopupRoute: Routes = [
{
path: 'bank-account/:id/delete',
component: BankAccountDeletePopupComponent,
resolve: {
bankAccount: BankAccountResolve
},
data: {
authorities: ['ROLE_USER'],
pageTitle: 'jhipsterSampleApplicationApp.bankAccount.home.title'
},
canActivate: [UserRouteAccessService],
outlet: 'popup'
}
];
具體實作由user-route-access-service.ts的
canActivate調用checkLogin實作權限的控制目的
5.前端界面元素通路控制
使用jhiHasAnyAuthority屬性
<button *jhiHasAnyAuthority="'ROLE_ADMIN'"
type="submit"
[routerLink]="['/', { outlets: { popup: 'bank-account/'+ bankAccount.id + '/delete'} }]"
replaceUrl="true"
queryParamsHandling="merge"
class="btn btn-danger btn-sm">
<fa-icon [icon]="'times'"></fa-icon>
<span class="d-none d-md-inline" jhiTranslate="entity.action.delete">Delete</span>
</button>
其實質是通過has-any-authority.directive.ts的updateView函數,調用this.accountService.hasAnyAuthority實作
6.前端ts通路控制
this.accountService.hasAnyAuthority(‘ROLE_ADMIN’)
二、權限改進設計
1.後端權限控制設計
a、定制權限通路控制服務,通過PreAuthorize控制通路
服務
@Service
public class SecurityService {
public boolean hasAccess(int parameter) {
return parameter == 1;
}
}
通路控制
@PreAuthorize("@securityService.hasAccess(#id)")
Message findOne(Long id);
b、Secured Filter攔截
通過繼承常用的FilterSecurityInterceptor方式實作
2.前端權限控制設計改進
a、定制權限通路控制服務,通過PreAuthorize控制通路
标題
@Injectable({ providedIn: 'root' })
export class UserRouteAccessService implements CanActivate {
constructor(
private router: Router,
private loginModalService: LoginModalService,
private accountService: AccountService,
private stateStorageService: StateStorageService
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const authorities = route.data['authorities'];
// We need to call the checkLogin / and so the accountService.identity() function, to ensure,
// that the client has a principal too, if they already logged in by the server.
// This could happen on a page refresh.
return this.checkLogin(authorities, state.url);
}
checkLogin(authorities: string[], url: string): Observable<boolean> {
return this.accountService.identity().pipe(
map(account => {
if (!authorities || authorities.length === 0) {
return true;
}
if (account) {
this.accountService.authenticate(account);
const hasAnyAuthority = this.accountService.hasAnyAuthority(authorities);
if (hasAnyAuthority) {
return true;
}
if (isDevMode()) {
console.error('User has not any of required authorities: ', authorities);
}
this.router.navigate(['accessdenied']);
return false;
}
this.stateStorageService.storeUrl(url);
this.router.navigate(['']);
this.loginModalService.open();
return false;
})
);
}
}