Spring Security(2)基於動態角色權限校驗
在上一章我們了解到了 Security 是怎么進行基礎配置的,下面我們要進行對數據庫訪問和權限分配的操作。
如果還不了解怎么進行基本配置請查閱:
這一節主要使用權限資源對請求權限進行管理,也是前后端分離最常用的權限校驗方式。希望大家看完會有所收獲。
數據庫結構設計
要實現權限控制首先得把數據庫設計好。
我這邊是這么設計的有一些字段在本案例中可能比較多余。僅供參考~
Security 配置調整
我們之前做了一些基礎的配置,現在在這個基礎的配置上進行改造。一步步打造成我們需要的功能。
1、將登錄成功、登錄失敗、退出登錄的處理類進行提取
2.1、登錄成功處理類
AdminAuthenticationSuccessHandler
@Component
public class AdminAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
// SecurityUser 是Spring Security里面UserDetailsd的實現類里面包含了用戶信息
SecurityUser securityUser = ((SecurityUser) authentication.getPrincipal());
// 從SecurityUser 中取出Token
String token = securityUser.getToken();
// 定義一個map集合 用於返回JSON數據
HashMap<String, String> map = new HashMap<String, String>(1);
// 前端會收到 {"token":"token值"}
map.put("token",token);
// 結果集封裝 規范返回內容
Object result = ApiResult.ok(map);
// 將封裝數據進行返回。 這里有一個返回工具類
ResponseUtils.renderJson(httpServletResponse, JacksonUtil.toJson(result));
}
}
2.2、SecrityUser 類
上面提到了 SecurityUser 類 ,這里貼一下我的代碼
SecurityUser
// 注解 避免JSON返回不必要的空數據
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecurityUser implements UserDetails {
/**
* 當前登錄用戶
*/
private User currentUserInfo;
/**
* 角色
*/
private List<Role> roleList;
/**
* 用戶權限值
*/
private List<Permission> permissionList;
private String token;
/**
* 空參構造
*/
public SecurityUser() {
}
/**
* 用戶信息構造
*
* @param user
*/
public SecurityUser(User user) {
if (user != null) {
this.currentUserInfo = user;
}
}
public SecurityUser(User user, List<Role> roleList) {
if (user != null) {
this.currentUserInfo = user;
this.roleList = roleList;
}
}
public SecurityUser(User user, List<Role> roleList, List<Permission> permissionList) {
if (user != null) {
this.currentUserInfo = user;
this.roleList = roleList;
this.permissionList = permissionList;
}
}
@Override
public String getPassword() {
return currentUserInfo.getPassword();
}
@Override
public String getUsername() {
return currentUserInfo.getUsername();
}
/**
* 用戶是否過期
*
* @return
*/
@Override
public boolean isAccountNonExpired() {
return false;
}
/**
* 用戶是否被鎖定
*
* @return true 用戶被鎖定 false用戶沒有被鎖定
*/
@Override
public boolean isAccountNonLocked() {
return false;
}
/**
* 賬戶登錄憑證是否過期
*
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return false;
}
/**
* 用戶是否被禁用
*
* @return true是 false否
*/
@Override
public boolean isEnabled() {
return false;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public User getCurrentUserInfo() {
return currentUserInfo;
}
public List<Role> getRoleList() {
return roleList;
}
public List<Permission> getPermissionList() {
return permissionList;
}
/**
* 獲取當前用戶具有的角色
* 用戶添加權限。返回權限對象
* 這個方法很重要,會影響到后面的權限資源控制
* @return GrantedAuthority
*/
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new HashSet<>();
// 判斷權限列表是否為空
if (!CollectionUtils.isEmpty(this.permissionList)) {
// 對權限列表進行遍歷
for (Permission permission : this.permissionList) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission.getAccessCode());
// 將權限添加到集合中
authorities.add(authority);
}
}
//返回權限
return authorities;
}
}
2.3、登錄失敗處理方法
AdminAuthenticationFailureHandler
@Slf4j
@Component
public class AdminAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
Object result;
// 對異常信息進行封裝處理返回給前端
if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),e.getMessage());
} else if (e instanceof LockedException) {
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"賬戶被鎖定,請聯系管理員!");
} else if (e instanceof CredentialsExpiredException) {
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"證書過期,請聯系管理員!");
} else if (e instanceof AccountExpiredException) {
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"賬戶過期,請聯系管理員!");
} else if (e instanceof DisabledException) {
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"賬戶被禁用,請聯系管理員!");
} else {
log.error("登錄失敗:", e);
result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"登錄失敗!");
}
ResponseUtils.renderJson(response, JacksonUtil.toJson(result));
}
}
2.4、退出登錄處理
AdminLogoutSuccessHandler
@Component
public class AdminLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Object result = ApiResult.ok("退出成功");
ResponseUtils.renderJson(httpServletResponse, result);
}
}
2、Security 配置類
SecurityConfig
@EnableWebSecurity
// 開啟注解權限資源控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定義認證處理器 (重點:額外添加的類,用於獲取用戶信息,保存用戶信息。並且處理登錄等操作)
*/
private final AdminAuthenticationProvider adminAuthenticationProvider;
/**
* 登錄成功處理器
*/
private final AdminAuthenticationSuccessHandler adminAuthenticationSuccessHandler;
/**
* 登錄失敗處理器
*/
private final AdminAuthenticationFailureHandler adminAuthenticationFailureHandler;
/**
* 未登錄情況下的處理類 (額外添加的類 這個類在下方有說明)
*/
private final AdminAuthenticationEntryPoint adminAuthenticationEntryPoint;
/**
* Token 處理器 (額外添加的類 這個類在下方有說明)
*/
private final MyTokenAuthenticationFilter myTokenAuthenticationFilter;
/**
* 退出登錄處理類 (我在這里處理了一下緩存的token退出的時候清除掉了,這個類下方也有說明)
*/
private final AdminLogoutHandler adminLogoutHandler;
/**
* 退出成功處理
*/
private final AdminLogoutSuccessHandler adminLogoutSuccessHandler;
// 上面是登錄認證相關 下面為url權限相關 - ========================================================================================
/**
* 登陸過后無權訪問返回處理類 (返回無權的時候的返回信息處理)
*/
private final MyAccessDeniedHandler myAccessDeniedHandler;
public SecurityConfig(AdminAuthenticationProvider adminAuthenticationProvider, AdminAuthenticationSuccessHandler adminAuthenticationSuccessHandler, AdminAuthenticationFailureHandler adminAuthenticationFailureHandler, AdminAuthenticationEntryPoint adminAuthenticationEntryPoint, MyTokenAuthenticationFilter myTokenAuthenticationFilter, AdminLogoutHandler adminLogoutHandler, AdminLogoutSuccessHandler adminLogoutSuccessHandler, MyAccessDeniedHandler myAccessDeniedHandler) {
this.myTokenAuthenticationFilter = myTokenAuthenticationFilter;
this.adminAuthenticationProvider = adminAuthenticationProvider;
this.adminAuthenticationSuccessHandler = adminAuthenticationSuccessHandler;
this.adminAuthenticationFailureHandler = adminAuthenticationFailureHandler;
this.adminAuthenticationEntryPoint = adminAuthenticationEntryPoint;
this.adminLogoutHandler = adminLogoutHandler;
this.adminLogoutSuccessHandler = adminLogoutSuccessHandler;
this.myAccessDeniedHandler = myAccessDeniedHandler;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();
// 標記只能在 服務器本地ip[127.0.0.1或者localhost] 訪問`/home`接口,其他ip地址無法訪問
// registry.antMatchers("/").hasIpAddress("127.0.0.1");
// 允許匿名的url - 可理解為放行接口 - 多個接口使用,分割
registry.antMatchers("/home").permitAll();
// OPTIONS(選項):查找適用於一個特定網址資源的通訊選擇。 在不需執行具體的涉及數據傳輸的動作情況下, 允許客戶端來確定與資源相關的選項以及 / 或者要求, 或是一個服務器的性能
// registry.antMatchers(HttpMethod.OPTIONS, Constants.CONTEXT_PATH+"/**").denyAll();
// 其余所有請求都需要認證
registry.anyRequest().authenticated();
// 禁用CSRF 開啟跨域
http.csrf().disable();
// 開啟CSRF 向前端發送 XSRF-TOKEN Cookie(上面設置了關閉,可以通過下述方式進行開啟,但是swagger-ui的跨域403異常暫時找不到解決辦法。無奈放棄了)
// http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
//配置HTTP基本身份認證
http.httpBasic();
// 未登錄認證異常
http.exceptionHandling().authenticationEntryPoint(adminAuthenticationEntryPoint);
// 登陸過后無權訪問返回
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
// 登錄處理 - 前后端一體的情況下
http.formLogin().loginProcessingUrl("/user/login")
// 默認的登錄成功返回url
// .defaultSuccessUrl("/")
// 登錄成功處理
.successHandler(adminAuthenticationSuccessHandler)
.failureHandler(adminAuthenticationFailureHandler)
// 自定義登陸用戶名和密碼屬性名,默認為 username和password
.usernameParameter("username").passwordParameter("password")
// 異常處理
// .failureUrl(Constants.CONTEXT_PATH+"/login/error").permitAll()
// 退出登錄
.and().logout().logoutUrl("/user/logout").permitAll()
.addLogoutHandler(adminLogoutHandler)
.logoutSuccessHandler(adminLogoutSuccessHandler);
// session創建規則 STATELESS 不使用session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 防止iframe 造成跨域
http.headers().frameOptions().disable();
// 添加前置的過濾器 用於驗證token
http.addFilterBefore(myTokenAuthenticationFilter, BasicAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 自定義驗證管理器
auth.authenticationProvider(adminAuthenticationProvider);
// super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
// ignoring 允許添加 RequestMatcher Spring Security 應該忽略的實例。
web.ignoring().antMatchers(HttpMethod.GET,
"/favicon.ico",
"/*.html",
"/**/*.css",
"/**/*.js");
web.ignoring().antMatchers(HttpMethod.GET,"/swagger-resources/**");
web.ignoring().antMatchers(HttpMethod.GET,"/webjars/**");
web.ignoring().antMatchers(HttpMethod.GET,"/v2/api-docs");
web.ignoring().antMatchers(HttpMethod.GET,"/v2/api-docs-ext");
web.ignoring().antMatchers(HttpMethod.GET,"/configuration/ui");
web.ignoring().antMatchers(HttpMethod.GET,"/configuration/security");
}
}
3、自定義認證處理器
用於從獲取用戶信息、保存用戶信息,並且處理登錄等操作。
AdminAuthenticationProvider
@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {
private final
UserDetailsServiceImpl userDetailsService;
private final RedisUtil redisUtil;
@Autowired
public AdminAuthenticationProvider(UserDetailsServiceImpl userDetailsService, RedisUtil redisUtil) {
this.userDetailsService = userDetailsService;
this.redisUtil = redisUtil;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 獲取前端表單中輸入后返回的用戶名、密碼
String userName = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
// 通過用戶名稱獲取用戶信息 (UserDetailsServiceImpl 這個類是實現了 Security 中提供的 UserDetailsService 用於處理用戶信息 具體內容在下方)
SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);
// 判斷用戶是否存在
if(userInfo == null){
throw new UsernameNotFoundException("當前用戶名不存在");
}
// 對可能出現的異常進行拋出,拋出的一次會跳到登錄失敗中進行集中處理
if(userInfo.isEnabled()){
throw new DisabledException("該賬戶已被禁用,請聯系管理員");
}else if(userInfo.isAccountNonLocked()){
throw new LockedException("該賬戶已被鎖定");
}else if(userInfo.isAccountNonExpired()){
throw new AccountExpiredException("該賬戶已過期,請聯系管理員");
}else if(userInfo.isCredentialsNonExpired()){
throw new CredentialsExpiredException("該賬戶的登陸憑證已過期,請重新登錄");
}
// 密碼驗證,用於驗證傳進來的密碼和加密后的密碼是否是同一個。
boolean isValid = PasswordUtils.checkpw(password, userInfo.getPassword());
if (!isValid) {
//拋出密碼異常
throw new BadCredentialsException("密碼錯誤!");
}
// 前后端分離情況下 處理邏輯...
// 更新登錄令牌 - 之后訪問系統其它接口直接通過token認證用戶權限...
String token = PasswordUtils.hashpw(System.currentTimeMillis() + userInfo.getUsername(), PasswordUtils.getSalt());
userInfo.setToken(token);
// 通過 redis 緩存數據
redisUtil.set(token,userInfo,30*60L);
return new UsernamePasswordAuthenticationToken(userInfo,password,userInfo.getAuthorities());
}
@Override
public boolean supports(Class<?> aClass) {
//確保 aClass 能轉成該類
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
上面有提到所以貼出來方便理解整個過程,這個類用於處理用戶數據,獲取用戶信息。
UserDetailsServiceImpl
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
private final UserRoleService userRoleService;
private final RoleService roleService;
private final RolePermissionService rolePermissionService;
private final PermissionService permissionService;
@Autowired(required = false)
public UserDetailsServiceImpl(UserMapper userMapper, RoleMapper roleMapper, PermissionMapper permissionMapper, UserRoleMapper userRoleMapper, RolePermissionMapper rolePermissionMapper, GroupPermissionMapper groupPermissionMapper, UserService userService, UserRoleService userRoleService, RoleService roleService, RolePermissionService rolePermissionService, PermissionService permissionService) {
this.userService = userService;
this.userRoleService = userRoleService;
this.roleService = roleService;
this.rolePermissionService = rolePermissionService;
this.permissionService = permissionService;
}
/**
* 根據用戶獲取用戶信息、角色信息、權限信息
*
* @param username:
* @return UserDetails
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 從數據庫中取出用戶信息
User user = userService.queryByUserName(username);
// 判斷用戶是否存在
if (user == null) {
throw new UsernameNotFoundException("用戶名不存在");
}
List<Role> userRoles = getUserRoles(user.getId());
List<Permission> userRolePermissions = getUserRolePermissions(userRoles);
// 返回 SecurityUser 這個類上面有介紹
return new SecurityUser(user,userRoles,userRolePermissions);
}
/**
* 根據用戶ID獲取角色權限信息
*
* @param id
* @return
*/
private List<Role> getUserRoles(Integer id) {
//通過用戶ID查詢角色中間表
List<UserRole> userRoles = userRoleService.queryAll(id);
//通過角色中間表查詢角色列表
List<Role> roles = roleService.queryByUserRoles(userRoles);
// 返回角色列表
return roles;
}
/**
* 通過角色列表獲取權限信息字符串
*
* @return
*/
private Set<String> getUserRolePermissionsAccessCode(List<Role> roleList) {
/**
* 通過角色權限中間表查詢
*/
List<RolePermission> rolePermissions = rolePermissionService.queryByRoleList(roleList);
return permissionService.queryByPermissionIdsOrAccessCode(rolePermissions);
}
/**
* 通過角色列表獲取權限信息
*
* @return
*/
private List<Permission> getUserRolePermissions(List<Role> roleList) {
/**
* 通過角色權限中間表查詢
*/
List<RolePermission> rolePermissions = rolePermissionService.queryByRoleList(roleList);
return permissionService.queryByPermissionIds(rolePermissions);
}
}
4、用戶 Token 處理
MyTokenAuthenticationFilter
@Slf4j
@Component
public class MyTokenAuthenticationFilter extends OncePerRequestFilter {
private final RedisUtil redisUtil;
public MyTokenAuthenticationFilter(RedisUtil redisUtil) {
this.redisUtil = redisUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
log.info("請求頭類型: {}" , httpServletRequest.getContentType());
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
// 記錄請求的消息體
logRequestBody(wrappedRequest);
// 前后端分離情況下,前端登錄后將token儲存在cookie中,每次訪問接口時通過token去拿用戶權限
// Constants 這個類是自己定義的常量類REQUEST_HEADER這個表示token頭的名稱
String token = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
log.debug("后台檢查令牌:{}", token);
if (StringUtils.isNotBlank(token)) {
// 檢查token
SecurityUser securityUser = (SecurityUser) redisUtil.get(token);
if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
throw new AccessDeniedException("TOKEN已過期,請重新登錄!");
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
// 全局注入角色權限信息和登錄用戶基本信息
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(wrappedRequest, wrappedResponse);
} catch (AuthenticationException e) {
log.error(e.getLocalizedMessage());
// 清空當前線程中的 SecurityContext
SecurityContextHolder.clearContext();
// 執行 認證失敗方法
this.adminAuthenticationEntryPoint.commence(wrappedRequest, wrappedResponse, e);
} finally {
stopWatch.stop();
long usedTimes = stopWatch.getTotalTimeMillis();
// 打印消息
logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
}
}
// 處理請求
private void logRequestBody(MultiReadHttpServletRequest request) {
MultiReadHttpServletRequest wrapper = request;
if (wrapper != null) {
try {
String bodyJson = wrapper.getBodyJsonStrByJson(request);
String url = wrapper.getRequestURI().replace("//", "/");
log.info("-------------------------------- 請求url: " + url + " --------------------------------");
Constants.URL_MAPPING_MAP.put(url, url);
log.info("`{}` 接收到的參數: {}",url , bodyJson);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 處理返回
private void logResponseBody(MultiReadHttpServletRequest request, MultiReadHttpServletResponse response, long useTime) {
MultiReadHttpServletResponse wrapper = response;
if (wrapper != null) {
byte[] buf = wrapper.getBody();
if (buf.length > 0) {
String payload;
try {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
} catch (UnsupportedEncodingException ex) {
payload = "[unknown]";
}
log.info("`{}` 耗時:{}ms 返回的參數: {}", Constants.URL_MAPPING_MAP.get(request.getRequestURI()), useTime, payload);
log.info("");
}
}
}
}
到這里其實已經基本上差不多了。
5、其他處理類
5.1、未登錄情況下的處理類
AdminAuthenticationEntryPoint.java
@Slf4j
@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
String message = e.getMessage();
Object result;
if(e instanceof AccountExpiredException){
// 判斷錯誤類型並且返回數據 主要處理的是Token過期問題
// 其中HttpStatus類是自己定義的一個枚舉類。用於定義狀態碼和消息
result = ApiResult.fail(HttpStatus.UNAUTHORIZED.code(),message);
}else{
result = ApiResult.fail(HttpStatus.UNAUTHORIZED.code(), HttpStatus.UNAUTHORIZED.reasonPhraseCN());
}
ResponseUtils.renderJson(httpServletResponse, JacksonUtil.toJson(result));
}
}
5.2、登陸過后無權訪問返回處理類
MyAccessDeniedHandler.java
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
ResponseUtils.renderJson(httpServletResponse, ApiResult.fail(403, e.getMessage()));
}
}
到這里配置項已經完成了。
6、在 Controller 中添加訪問權限
這里我簡單寫了一下 UserController.java
@RestController
@RequestMapping("/api/user")
@Api(value = "用戶相關操作",tags="用戶相關操作")
public class UserController {
private final
UserMapper userMapper;
@Autowired(required = false)
public UserController(UserMapper userMapper) {
this.userMapper = userMapper;
}
// 需要注意這里的權限控制值,需要與數據庫中獲取到的值一致
@PreAuthorize("hasAuthority('user')")
@PostMapping("/userInfo")
@ApiOperation(value = "查詢用戶信息",notes = "查詢用戶信息請求頭里面需要攜帶X-Token")
UserInfo getUserInfo() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserInfo userInfo = null;
if(authentication != null){
SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
userInfo = new UserInfo();
userInfo.setUser(securityUser.getCurrentUserInfo());
userInfo.setRoles(securityUser.getRoleList());
userInfo.setPermissions(securityUser.getPermissionList());
userInfo.setPermissionAccessList(securityUser.getPermissionAccessCode());
}
return userInfo;
}
@PreAuthorize("hasAuthority('user_add')")
@PostMapping("/addUser")
@ApiOperation(value = "添加用戶",notes = "添加用戶信息")
@ApiImplicitParam()
Object addUser(@RequestBody User user){
user.setPassword(PasswordUtils.hashpw(user.getPassword(),PasswordUtils.getSalt()));
userMapper.insertSelective(user);
return ApiResult.ok("操作成功");
}
@PreAuthorize("hasAuthority('no_access')")
@PostMapping("/noAccess")
@ApiImplicitParam()
Object noAccess(){
return ApiResult.ok("無權訪問的接口");
}
}
需要注意: @PreAuthorize("hasAuthority('user')")
該注解里面的值需要與數據庫里面的值保持一致
對上述的功能進行測試
寫了這么多,也要對實際結果進行確認才行。我這里用的 Postman 測試一下。
首先需要登錄獲取 token
通過登錄接口可以拿到token,token值需要放到請求頭中。用於認證用戶是否登錄。
再測試一下其他接口
-
獲取用戶信息(注意這里設置了 token 請求頭)
-
訪問無權接口
這樣的話就差不多是最終的效果了。多嘗試多進步,方法總比困難多。
如果有更好的處理方式可以提出來相互交流~