Spring Security(2)基於動態角色資源權限校驗


Spring Security(2)基於動態角色權限校驗


在上一章我們了解到了 Security 是怎么進行基礎配置的,下面我們要進行對數據庫訪問和權限分配的操作。
如果還不了解怎么進行基本配置請查閱:

Spring Security(1)入門體驗


這一節主要使用權限資源對請求權限進行管理,也是前后端分離最常用的權限校驗方式。希望大家看完會有所收獲。


數據庫結構設計

要實現權限控制首先得把數據庫設計好。


image.png


我這邊是這么設計的有一些字段在本案例中可能比較多余。僅供參考~


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

image.png

通過登錄接口可以拿到token,token值需要放到請求頭中。用於認證用戶是否登錄。


再測試一下其他接口

  • 獲取用戶信息(注意這里設置了 token 請求頭)
    image.png

  • 訪問無權接口
    image.png


這樣的話就差不多是最終的效果了。多嘗試多進步,方法總比困難多。
如果有更好的處理方式可以提出來相互交流~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM