溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring?Security權(quán)限管理實(shí)例分析

發(fā)布時間:2022-08-11 14:04:02 來源:億速云 閱讀:156 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Spring Security權(quán)限管理實(shí)例分析”,在日常操作中,相信很多人在Spring Security權(quán)限管理實(shí)例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring Security權(quán)限管理實(shí)例分析”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

    1 Spring Security配置用戶名和密碼

    方式一:在application.properties文件中配置

    # 配置security用戶名密碼
    spring.security.user.password=LIFEILIN
    spring.security.user.name=LIFEILIN
    spring.security.user.roles=admin

    方式二:代碼配置

    @Configuration
    public class securityConfig extends WebSecurityConfigurerAdapter {
    
        //暫且密碼不加密
        @Bean
        PasswordEncoder passwordEncoder(){
            return NoOpPasswordEncoder.getInstance();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("LIFEILIN").password("LIFEILIN").roles("admin")   //第一個
                    .and()
                    .withUser("123").password("123").roles("user"); //第二個
        }
    }

    2 HttpSecurity的配置

            //配置HttpSecurity攔截規(guī)則
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()    //開啟配置
                    .antMatchers("/admin/**").hasRole("admin")
                    .antMatchers("/user/**").hasAnyRole("admin","user")
                    .anyRequest().authenticated()  //其他請求登錄后即可訪問
                    .and()
                    .formLogin()
                    .loginProcessingUrl("/doLogin")
                    .permitAll()    //跟登錄相關(guān)接口直接訪問
                    .and()
                    .csrf().disable();
            }

    3 登錄/注銷表單詳細(xì)配置

        //配置HttpSecurity攔截規(guī)則
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()    //開啟配置
                    .antMatchers("/admin/**").hasRole("admin")
                    .antMatchers("/user/**").hasAnyRole("admin", "user")
                    .anyRequest().authenticated()  //其他請求登錄后即可訪問
                    .and()
                    .formLogin()
                    .loginProcessingUrl("/doLogin")
    //                .loginPage("login") //登錄頁面
                    //自定義用戶名密碼
                    .usernameParameter("uname")
                    .passwordParameter("passwd")
                    //登錄成功的處理器(前后端分離)
                    .successHandler(new AuthenticationSuccessHandler() {
                        @Override
                        public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException { //authentication為登錄成功對象
                            //登錄成功,返回json
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            Map<String, Object> map = new HashMap<>();
                            map.put("status", 200);
                            map.put("msg", authentication.getPrincipal());   //登錄成功對象
                            out.write(new ObjectMapper().writeValueAsString(map));  //將map轉(zhuǎn)為json寫出去
                            out.flush();
                            out.close();
                        }
                    })
                    //登錄失敗的處理器(前后端分離)
                    .failureHandler(new AuthenticationFailureHandler() {
                        @Override
                        public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            Map<String, Object> map = new HashMap<>();
                            map.put("status", 401);
                            if (e instanceof LockedException){  //賬號鎖定
                                map.put("msg","賬號被鎖定,登錄失敗");
                            }else if (e instanceof BadCredentialsException){
                                map.put("msg","用戶名和密碼輸入錯誤,登錄失敗");
                            }else if (e instanceof DisabledException){
                                map.put("msg","賬號被禁用,登錄失敗");
                            }else if (e instanceof AccountExpiredException){
                                map.put("msg","賬戶過期,登錄失敗");
                            }else if (e instanceof CredentialsExpiredException){
                                map.put("msg","密碼過期,登錄失敗");
                            }else {
                                map.put("msg","登錄失敗");
                            }
                            out.write(new ObjectMapper().writeValueAsString(map));  //將map轉(zhuǎn)為json寫出去
                            out.flush();
                            out.close();
                        }
                    })
                    .permitAll()    //跟登錄相關(guān)接口直接訪問
                    .and()
                    //注銷登錄
                    .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessHandler(new LogoutSuccessHandler() {
                        @Override
                        public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            Map<String, Object> map = new HashMap<>();
                            map.put("status", 200);
                            map.put("msg", "注銷登錄成功");   //注銷登錄成功
                            out.write(new ObjectMapper().writeValueAsString(map));  //將map轉(zhuǎn)為json寫出去
                            out.flush();
                            out.close();
                        }
                    })
                    .and()
                    .csrf().disable();
        }

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    4 多個HttpSecurity的配置

    配置類不需要繼承WebSecurityConfigurerAdapter方法,直接注入:configure方法

    @Configuration
    public class MultiHttpSecurityConfig {
        //暫且密碼不加密
        @Bean
        PasswordEncoder passwordEncoder() {
            return NoOpPasswordEncoder.getInstance();
        }
    
        //配置用戶名和密碼
        @Autowired
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("LIFEILIN").password("LIFEILIN").roles("admin")   //第一個
                    .and()
                    .withUser("123").password("123").roles("user"); //第二個
        }
    
        @Configuration
        @Order(1)
        public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasRole("admin"); //admin角色訪問
            }
        }
    
        @Configuration
        public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter {
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests().anyRequest().authenticated()
                        .and()
                        .formLogin()
                        .loginProcessingUrl("/doLogin")
                        .permitAll()
                        .and()
                        .csrf().disable();
            }
        }
    }

    5 密碼加密

    相同的明文可加密成不同的密文,不用維護(hù)原字段。

    @Test
    void contextLoads() {
        for (int i=0;i<10;i++){
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            System.out.println(encoder.encode("123"));
    
        }
    }

    明文【123】加密后:

    $2a 10 10 10SS.YDon5lzqkIFdW8DQYzOTJBvQwkdXHWcHlIfF1fa/wPjJtru5aO
    $2a 10 10 10vJsPq4GBtHKmmBQaKTriTO90sFurCEDavZANqCoqGu4gAzXxGLbTC
    $2a 10 10 10gZ4H3/tBRpz2lPX0XUI1ber2qsNsKuk38j0iSsATeVOrrWFJIEr1G
    $2a 10 10 10h7RiyAXP8JzWGsmAXGZy/uO6ASraQPNryVPl.11vMyUjhSCxS.Sde
    $2a 10 10 10BCm3vuueGWdvjG3ciCUZB.6V9y6jMELHqB9iv2DwRJyOkR5jd&hellip;4S
    $2a 10 10 10rO2894WmxRMtjHVzoYivyuzvje8BrAUjm8YLj3K.i4sQDvpWBtuuy
    $2a 10 10 10jTosyN75hwKB3OSQCYY9YOIj6TYZG1FdJXfYCalTUuXpPiI5tv/P.
    $2a 10 10 10p95j18H3yRABEScCE/2MqOqYt1ZqArdYhC87BVGEmQvn6znSqKw5G
    $2a 10 10 10/y8FGBlvod1Dnq29c2scs.eGnYfvezZIZwfDHoXFfgIVA7H0T17pO
    $2a 10 10 10k8IKAv4dBXhooEU8Qgo6E.PcrQ/ICymqNGLyE8Jfo4V1nk61GMeuy

    Spring?Security權(quán)限管理實(shí)例分析

    6 方法安全

    在配置類中添加注解:@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)

    接口都能訪問,但進(jìn)了接口不一定能訪問到接口里面的方法!!
    【Controller層:】

    @Autowired
    MethodService methodService;
    
    @GetMapping("/hello1")
    public String hello1(){
        return methodService.admin();
    }
    @GetMapping("/hello2")
    public String hello2(){
        return methodService.user();
    }
    @GetMapping("/hello3")
    public String hello3(){
        return methodService.hello();
    }

    【Service層:】

    @Service
    public class MethodService {
        @PreAuthorize("hasRole('admin')")
        public String admin() {  //需要admin角色才能訪問
            return "hello admin";
        }
    
        @Secured("ROLE_user")
        public String user(){   //需要user角色才能訪問
            return "hello user";
        }
    
        @PreAuthorize("hasAnyRole('admin','user')") //admin,user兩種權(quán)限
        public String hello(){
            return "hello hello";
        }
    }

    7 基于數(shù)據(jù)庫的認(rèn)證

    1、數(shù)據(jù)庫中創(chuàng)建三張表user、role、user_role

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    2、設(shè)置配置文件

    # 應(yīng)用名稱
    spring.application.name=SpringBoot_11_security
    # 應(yīng)用服務(wù) WEB 訪問端口
    server.port=8080
    #下面這些內(nèi)容是為了讓MyBatis映射
    # 指定Mybatis的Mapper文件
    mybatis.mapper-locations=classpath:mappers/*xml
    # 指定Mybatis的實(shí)體目錄
    mybatis.type-aliases-package=com.example.mybatis.entity
    # 數(shù)據(jù)庫驅(qū)動:
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    # 數(shù)據(jù)源名稱
    spring.datasource.name=defaultDataSource
    # 數(shù)據(jù)庫連接地址
    spring.datasource.url=jdbc:mysql://localhost:3306/【數(shù)據(jù)庫名稱】?serverTimezone=UTC
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    # 數(shù)據(jù)庫用戶名&密碼:
    spring.datasource.username=root
    spring.datasource.password=【數(shù)據(jù)庫密碼】

    3、創(chuàng)建實(shí)體User、Role

    package com.example.bean;
    
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    /**
     * @author 李飛林
     * @ClassName User
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 21:46
     */
    public class User implements UserDetails {
        private Integer id;
        private String username;
        private String password;
        private Boolean enabled;
        private Boolean locked;
        private List<Role> roles;
    
        public List<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(List<Role> roles) {
            this.roles = roles;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public void setEnabled(Boolean enabled) {
            this.enabled = enabled;
        }
    
        public void setLocked(Boolean locked) {
            this.locked = locked;
        }
    
        public Integer getId() {
            return id;
        }
    
        @Override
        public String getUsername() {
            return username;
        }
    
        @Override
        public boolean isAccountNonExpired() {  //賬號是否未過期
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {   //賬號是否未鎖定
            return !locked;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {    //是否可用
            return enabled;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<SimpleGrantedAuthority> authorities=new ArrayList<>();
            for (Role role:roles){
                authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));//角色認(rèn)證以ROLE_開始
            }
            return authorities;    //返回用戶所有角色
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    }
    package com.example.bean;
    
    /**
     * @author 李飛林
     * @ClassName Role
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 21:49
     */
    public class Role {
        private Integer id;
        private String name;
        private String nameZh;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getNameZh() {
            return nameZh;
        }
    
        public void setNameZh(String nameZh) {
            this.nameZh = nameZh;
        }
    }

    4、編寫mapper層
    UserMapper接口:

    package com.example.mapper;
    
    import com.example.bean.Role;
    import com.example.bean.User;
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    /**
     * @author 李飛林
     * @ClassName UserMapper
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 22:01
     */
    @Mapper
    public interface UserMapper {
        User loadUserByUsername(String username);
    
        List<Role> getUserRolesById(Integer id);
    }

    UserMapper.xml:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.example.mapper.UserMapper">
        <select id="loadUserByUsername" resultType="com.example.bean.User">
            select *
            from user
            where username = #{username};
        </select>
        <select id="getUserRolesById" resultType="com.example.bean.Role">
            select *
            from role
            where id in (select rid from user_role where uid = #{id})
        </select>
    </mapper>

    5、編寫service層:

    package com.example.service;
    
    import com.example.bean.User;
    import com.example.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    
    /**
     * @author 李飛林
     * @ClassName UserService
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 22:01
     */
    @Service
    public class UserService implements UserDetailsService {
        @Autowired
        UserMapper userMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userMapper.loadUserByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException("用戶不存在");
            }
            user.setRoles(userMapper.getUserRolesById(user.getId()));
            return user;
        }
    }

    6、security安全配置:

    package com.example.config;
    
    import com.example.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * @author 李飛林
     * @ClassName SecurityConfig
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 22:35
     */
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        UserService userService;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userService);
        }
    
        @Bean
        PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/dba/**").hasRole("dba")
                    .antMatchers("/admin/**").hasRole("admin")
                    .antMatchers("/user/**").hasRole("user")
                    .anyRequest().authenticated()//其他可訪問
                    .and()
                    .formLogin()
                    .permitAll()
                    .and()
                    .csrf().disable();
        }
    }

    7、controller層接口調(diào)試:

    package com.example.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 李飛林
     * @ClassName HelloController
     * @mail 1961785612@qq.com
     * @Description TODO
     * @date 2022/8/4 22:40
     */
    @RestController
    public class HelloController {
        @GetMapping("/hello")
        public String hello() {
            return "hello security";
        }
    
        @GetMapping("/dba/hello")
        public String dba() {
            return "hello dba";
        }
    
        @GetMapping("/admin/hello")
        public String admin() {
            return "hello admin";
        }
    
        @GetMapping("/user/hello")
        public String user() {
            return "hello user";
        }
    }

    8 角色繼承(在securityConfig中加入代碼段)

    //角色繼承
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_dba > ROLE_admin > ROLE_user"; //dba > admin > user
        roleHierarchy.setHierarchy(hierarchy);
        return roleHierarchy;
    }

    9 動態(tài)配置權(quán)限

    數(shù)據(jù)庫中的表結(jié)構(gòu)如下:

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    Spring?Security權(quán)限管理實(shí)例分析

    其中菜單表中已經(jīng)配置好對應(yīng)的路徑,后面需要從數(shù)據(jù)庫中加載:

    Spring?Security權(quán)限管理實(shí)例分析

    一、查詢user用戶所具有的角色

    1、編寫實(shí)體類User、Role、Menu:
    User實(shí)現(xiàn)UserDetails接口,實(shí)現(xiàn)如下方法:

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities=new ArrayList<>();
        for (Role role :
                roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    2、編寫UserService:繼承UserDetailsService接口,實(shí)現(xiàn)loadUserByUsername方法

    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, UserDetailsService {
        @Autowired
        UserMapper userMapper;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userMapper.loadUserByUsername(username);	//根據(jù)登錄字符串獲獲取用戶名
            if (user == null) {
                throw new UsernameNotFoundException("用戶不存在");
            } else {
                user.setRoles(userMapper.getRolesById(user.getId()));	//根據(jù)用戶名的ID查詢所具有的角色
            }
            return user;
        }
    }

    3、編寫UserMapper接口:

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    
        User loadUserByUsername(String username);
    
        List<Role> getRolesById(Integer id);
    }

    4、編寫UserMapper.xml:

    <select id="loadUserByUsername" resultType="com.lifeilin.pojo.User">
        select *
        from user
        where username = #{username}
    </select>
    
    <select id="getRolesById" resultType="com.lifeilin.pojo.Role">
        select *
        from role
        where id in (select rid from user_role where uid = #{id});
    </select>

    至此,已經(jīng)從數(shù)據(jù)庫中獲取到登錄用戶user所具備的角色

    二、配置SecurityConfig

    1、在SecurityConfig類中配置登錄權(quán)限

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        UserServiceImpl userService;
    
        @Bean
        PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
        
    	//配置登錄
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userService);
        }
    }

    2、配置角色(從數(shù)據(jù)庫中動態(tài)加載) 1 在config包中創(chuàng)建MyFilter.java過濾器

    在config包中創(chuàng)建MyFilter.java過濾器,實(shí)現(xiàn)FilterInvocationSecurityMetadataSource接口,其主要作用是分析請求地址,請求地址必然是menu表中給出的標(biāo)準(zhǔn)地址(如果不是則進(jìn)行其他操作),根據(jù)請求地址分析出需要哪些角色

    注意:這里需要提前從數(shù)據(jù)庫查詢出所有菜單以及對應(yīng)的角色。

    補(bǔ)充:查詢菜單及對應(yīng)角色(使用Spring Cache作緩存)

    1、導(dǎo)入緩存相關(guān)依賴

    <!--        redis依賴-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--        cache依賴-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    2、需要簡單配置一下Redis,Redis的基本信息,另外,這里要用到Cache,因此還需要稍微配置一下Cache,如下:

    ## 配置redis
    #基本屬性
    spring.redis.host=localhost
    spring.redis.port=6379
    spring.redis.database=0
    spring.redis.password=
    #配置cache名稱
    spring.cache.cache-names=c1

    另外,還需要在配置類上添加如下代碼,表示開啟緩存:

    Spring?Security權(quán)限管理實(shí)例分析

    3、Service層緩存的使用
    (1)在MenuServiceImpl類上使用@CacheConfig(cacheNames = “c1”)
    這個注解在類上使用,用來描述該類中所有方法使用的緩存名稱,當(dāng)然也可以不使用該注解,直接在具體的緩存注解上配置名稱。
    (2)在MenuServiceImpl類下getAllMenus()方法使用@Cacheable
    這個注解一般加在查詢方法上,表示將一個方法的返回值緩存起來,默認(rèn)情況下,緩存的key就是方法的參數(shù),緩存的value就是方法的返回值。

    @Service
    @CacheConfig(cacheNames = "c1")
    public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
        @Autowired
        MenuMapper menuMapper;
    
        //可以加緩存
        @Cacheable
        public List<Menu> getAllMenus() {
            return menuMapper.getAllMenus();
        }
    }
    @Mapper
    public interface MenuMapper extends BaseMapper<Menu> {
    
        List<Menu> getAllMenus();
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lifeilin.mapper.MenuMapper">
        <!--    查詢所有menu-->
        <!-- resultMap:填入配置的resultMap標(biāo)簽的id值 -->
        <select id="getAllMenus" resultMap="BaseResultMap">
            SELECT m.id,
                   m.pattern,
                   r.id     AS rid,
                   r.NAME   AS rname,
                   r.nameZh AS rnameZh
            FROM menu AS m
                     LEFT JOIN menu_role AS mr ON m.id = mr.mid
                     LEFT JOIN role AS r ON mr.rid = r.id
        </select>
        <!-- resultMap最終還是要將結(jié)果映射到pojo上,type就是指定映射到哪一個pojo -->
        <resultMap id="BaseResultMap" type="com.lifeilin.pojo.Menu">
            <!-- 定義主鍵 ,非常重要。如果是多個字段,則定義多個id -->
            <!-- property:主鍵在pojo中的屬性名 -->
            <!-- column:主鍵在數(shù)據(jù)庫中的列名 -->
            <id property="id" column="id"></id>
            <!-- 定義普通屬性 -->
            <result property="pattern" column="pattern"></result>
            <!--collection中property的roles 對應(yīng)的是Role實(shí)體中的屬性-->
            <collection property="roles" ofType="com.lifeilin.pojo.Role">
                <id column="rid" property="id"/>
                <result column="rname" property="name"/>
                <result column="rnameZh" property="nameZh"/>
            </collection>
        </resultMap>
    </mapper>
    @Component
    public class MyFilter implements FilterInvocationSecurityMetadataSource {
        //路徑匹配符
        AntPathMatcher pathMatcher = new AntPathMatcher();
    
        @Autowired
        MenuServiceImpl menuService;
    
        @Override
        public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
            String requestUrl = ((FilterInvocation) object).getRequestUrl();//獲取請求的地址
            List<Menu> allMenus = menuService.getAllMenus();//查詢所有菜單
            for (Menu menu : allMenus) {
                if (pathMatcher.match(menu.getPattern(), requestUrl)) { //請求地址與菜單地址匹配上
                    List<Role> roles = menu.getRoles(); //獲取匹配成功的地址的角色
                    String[] rolesStr = new String[roles.size()];
                    for (int i = 0; i < roles.size(); i++) {
                        rolesStr[i] = roles.get(i).getName();
                    }
                    return SecurityConfig.createList(rolesStr);
                }
            }
            return SecurityConfig.createList("ROLE_login"); //沒有匹配上,標(biāo)記符,額外處理
        }
    
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    }

    2、在config包中創(chuàng)建MyAccessDecisionManager類

    在config包中創(chuàng)建MyAccessDecisionManager類,目的是通過上一步獲取了請求路徑需要哪些角色看看數(shù)據(jù)庫中是否具有該角色。

    @Component
    public class MyAccessDecisionManager implements AccessDecisionManager {
        @Override
        public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { //authentication知道有哪些角色,configAttributes知道需要哪些角色
            //1、遍歷需要的角色
            for (ConfigAttribute attribute : configAttributes) {
                if ("ROLE_login".equals(attribute.getAttribute())){//請求地址都沒匹配上,說明是登陸后就可訪問的請求地址
                    if (authentication instanceof AnonymousAuthenticationToken){    //匿名用戶(沒登陸)
                        throw new AccessDeniedException("非法請求");
                    }else {
                        return;
                    }
                }
                //2、獲取所具備的角色
                Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                for (GrantedAuthority authority : authorities) {
                    if (authority.getAuthority().equals(attribute.getAttribute())){//如果具備所需要的角色
                        return;
                    }
                }
            }
            throw new AccessDeniedException("非法請求");
        }
    
        @Override
        public boolean supports(ConfigAttribute attribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    }

    3、在SecurityConfig引入myAccessDecisionManager + myFilter

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(myAccessDecisionManager);//
                        object.setSecurityMetadataSource(myFilter);//
                        return object;
                    }
                })
                .and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable();
    }

    到此,關(guān)于“Spring Security權(quán)限管理實(shí)例分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI