spring-security安全框架(超精细版附带流程讲解图)

avatar
作者
筋斗云
阅读量:0

目录

一、回顾一下

二、security使用

2.1 覆盖掉默认配置「自定义配置」

2.2 如何自定义认证

2.3 纯纯自定义

2.4 jwt

2.5 官网认证流程

2.6 RBAC模型

4.1. 创建表结构

2.7 如何实现权限流程


一、回顾一下

  1. security干啥的?

    认证和授权

  2. 使用方式

    1. 引入依赖, 基于spring boot的下的使用.

    2. spring-boot-starter-security, 直接可以使用了.

  3. 观察一下

    1. 姿源分类

      1. 受保护的资源, 需要认证

      2. 公共方式, 不需要认证.

    2. 当我们把security引入到项目当中的时候,我们去访问一下受保护的资源,会弹出一个默认的一个登录界面.用户名称默认的是: user, 密码随机生成的.通过uuid生成的.如果认证成功,则直接跳转到要访问的接口.

  4. 基本原理

    1. SecurityAutoConfiguration, spring security自动配置类.默认配置.如果我们啥也不干,则直接走默认配置.界面了,用户名称和密码都是默认生成的.

    2. 如果想要覆盖掉默认配置,则我们用两种方案.

      1. 继承一个类WebSecurityConfigurerAdapter, 重写方法.

      2. 将SecurityFilterChain放到容器当中.

      /**  * {@link Condition} for  * {@link ConditionalOnDefaultWebSecurity @ConditionalOnDefaultWebSecurity}.  *  * @author Phillip Webb  */ class DefaultWebSecurityCondition extends AllNestedConditions { ​     DefaultWebSecurityCondition() {         super(ConfigurationPhase.REGISTER_BEAN);     } ​     @ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })     static class Classes { ​     } ​     // 当IoC容器当中没有WebSecurityConfigurerAdapter.class, SecurityFilterChain.class 这两个类的对象     // 则默认生效,否则默认配置不生效.     @ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class, SecurityFilterChain.class })     static class Beans { ​     } ​ }

    3. 默认的配置类

      SecurityProperties

      @ConfigurationProperties(prefix = "spring.security") public class SecurityProperties {}

    4. 对认证资源进行配置

      1. 可以针对某一些资源,不进行认证, 默认是都进行认证的.

      2. 此时我们就覆盖掉默认配置.

二、security使用

2.1 覆盖掉默认配置「自定义配置」

public HttpSecurity authorizeRequests(         Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer)         throws Exception {     ApplicationContext context = getContext();     authorizeRequestsCustomizer             .customize(getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry());     return HttpSecurity.this; }

认证成功之后的处理:

public final T successHandler(AuthenticationSuccessHandler successHandler) {     this.successHandler = successHandler;     return getSelf(); }
package com.tingyi.configs; ​ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; ​ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; ​ /**  * @author 听忆  */ @Configuration public class SecurityConfig { ​     @Bean     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {         return http.authorizeRequests(authorize ->                         authorize.mvcMatchers("/tom")                                 .permitAll()                                 .anyRequest()                                 .authenticated())                 .formLogin()                 // .successForwardUrl("/success") // 默认的话,跳转到你在认证之前的请求.                 // .defaultSuccessUrl("/success", true) // true,表示强制跳转到指定的url                 .successHandler(new AuthenticationSuccessHandler() { // security提供给我们的,认证成功之后的处理.我们可以在这里返回json给前端.                        // 前后端分离项目使用的方式;                     @Override                     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {                         // 给前端返回一个json串.应用于前后端分离的项目.                         Map<String, Object> map = new HashMap<>();                         map.put("code", 0); // 状态码                         map.put("msg", "认证成功");                         map.put("authentication", authentication); ​                         PrintWriter writer = response.getWriter();                         String json = new ObjectMapper().writeValueAsString(map);                         writer.print(json);                     }                 }).and()                 .csrf(csrf -> csrf.disable())                 .build();     } }

认证流程:

  • 浏览器输入了用户名称和密码 —> 服务器 –> security 进行认证, 怎么认证的?

    • UsernamePasswordAuthenticaionFilter

      • AbstractAuthenticationProcessingFilter


  • 我们去认证的时候,服务器把密码存储到哪里地了.

  • UserDetailsService

    • UserDetailsManager, 用户信息管理.接口.封装了对用户所有操作.

      • InMemoryUserDetailsManager, 基于内存实现的.也就是说,将用户信息都存储在内存当中了.

2.2 如何自定义认证

DaoAuthenticationProvider

protected void additionalAuthenticationChecks(UserDetails userDetails,         UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {     if (authentication.getCredentials() == null) {         this.logger.debug("Failed to authenticate since no credentials provided");         throw new BadCredentialsException(this.messages                 .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));     }     String presentedPassword = authentication.getCredentials().toString();     if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {         this.logger.debug("Failed to authenticate since password does not match stored value");         throw new BadCredentialsException(this.messages                 .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));     } }

数据放到了内存当中,使用的是: InMemoryUserDetailsManager, 从内存读取数据,实际开发当中,数据源, 一般情况来自于数据库.也就是说, 我们存储用户名称和密码应该是存储在数据当中,咱们进行认证的时候,应该是从数据当中获取用户名称和密码.替换掉默认的: InMemoryUserDetailsManager.

通过查看,类关系图.发现有一个接口: UserDetailsService

package org.springframework.security.core.userdetails; // 如果我们要自定义实现读取的数据源, 则必须实现这个接口,重写这个方法. public interface UserDetailsService {     // 通过用户名称获取用户的详细信息.     // 返回值是一个UserDetails接口.实际上返回的应该是一个对象.     UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

​ ​  
package org.springframework.security.core.userdetails; ​ import java.io.Serializable; import java.util.Collection; ​ import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; ​ /**  * Provides core user information.  *  * <p>  * Implementations are not used directly by Spring Security for security purposes. They  * simply store user information which is later encapsulated into {@link Authentication}  * objects. This allows non-security related user information (such as email addresses,  * telephone numbers etc) to be stored in a convenient location.  * <p>  * Concrete implementations must take particular care to ensure the non-null contract  * detailed for each method is enforced. See  * {@link org.springframework.security.core.userdetails.User} for a reference  * implementation (which you might like to extend or use in your code).  *  * @author Ben Alex  * @see UserDetailsService  * @see UserCache  * 用户详细信息  *  1. 用户名称  *  2. 用户密码  *  3. 用户的权限列表  *      1. 角色信息  *      2. 权限信息  */ public interface UserDetails extends Serializable { ​     /**      * Returns the authorities granted to the user. Cannot return <code>null</code>.      * @return the authorities, sorted by natural key (never <code>null</code>)      * 权限列表      */     Collection<? extends GrantedAuthority> getAuthorities(); ​     /**      * Returns the password used to authenticate the user.      * @return the password      * 获取用户密码      */     String getPassword(); ​     /**      * Returns the username used to authenticate the user. Cannot return      * <code>null</code>.      * @return the username (never <code>null</code>)      * 用户名称      */     String getUsername(); ​     /**      * Indicates whether the user's account has expired. An expired account cannot be      * authenticated.      * @return <code>true</code> if the user's account is valid (ie non-expired),      * <code>false</code> if no longer valid (ie expired)      * 账号状态是否是过期的.      */     boolean isAccountNonExpired(); ​     /**      * Indicates whether the user is locked or unlocked. A locked user cannot be      * authenticated.      * @return <code>true</code> if the user is not locked, <code>false</code> otherwise      */     boolean isAccountNonLocked(); ​     /**      * Indicates whether the user's credentials (password) has expired. Expired      * credentials prevent authentication.      * @return <code>true</code> if the user's credentials are valid (ie non-expired),      * <code>false</code> if no longer valid (ie expired)      */     boolean isCredentialsNonExpired(); ​     /**      * Indicates whether the user is enabled or disabled. A disabled user cannot be      * authenticated.      * @return <code>true</code> if the user is enabled, <code>false</code> otherwise      */     boolean isEnabled(); ​ }

User, spring security提供的一个类,这个类实现了UserDetails接口.

// username,表示我们根据用户名称,从内存或者数据库查询出来的用户名称. // password, 从内存或者数据库当中查询出来的密码 // authorities, 从内存或者数据库当中查询出来该用户名称对应的权限列表. public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {     this(username, password, true, true, true, true, authorities); }

重要的接口和实现类:

  • UserDetailsService,

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; // 根据前端传递过来的用户名称,去数据当中查询出用户名称对应的详细信息,封装成UserDetails对象即可;
    • InMemoryUserDetailsManager,它是一个实现类,它表示从内存当中读取.

    • 我们如果要换成从数据库当中读取用户信息,则必须实现UserDetailsService接口,重写方法.查询出来的数据,封里成UserDetatils对象.

  • UserDetails, 表示定义用户的各种各样的信息.

    • 用户名称

    • 用户密码

    • 用户权限列表

    • 实现类: User, 在UserDetailsService方法, loadUserByUsername返回它即可;

如果, controller当中的login,直接调用Service层,此时需要我们自己处理,整个验证过程.

现在我们如果在userDetailsService实现类当中,进行相关的业务处理,将验证过程直接交给了security. 不用我们操心了.

2.3 纯纯自定义

  1. 根据流程来说, 要将从内存获取数据方式改更从数据库进行查询.

    自己定义一个UserDetailsService实现类,完成一个逻辑:

    ①. 根据用户名称去数据库查询出这个用户名称对应的数据.

    ②. 将查询出来的数据封装成UserDetails对象.

package com.tingyi.service.impl; ​ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.qf.entity.TbUser; import com.qf.mapper.ITbUserMapper; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; 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; ​ import java.util.Collections; import java.util.List; ​ /**  * @author 听忆  * 自定义读取过程,之前是从在内存当中,根据用户名称获取用户详情,现在我们从数据库当中进行获取.  * mybatis plus 来读取一下.  */ @Service public class UserDetailsServiceImpl implements UserDetailsService {     private final ITbUserMapper tbUserMapper; ​     public UserDetailsServiceImpl(ITbUserMapper tbUserMapper) {         this.tbUserMapper = tbUserMapper;     } ​     @Override     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {         // 根据用户名称去数据库当中查找.         LambdaQueryWrapper<TbUser> queryWrapper = new LambdaQueryWrapper<>();         queryWrapper.eq(TbUser::getUsername, username);         // 查询出结果,根据咱们自己就没有处理它.         TbUser user = tbUserMapper.selectOne(queryWrapper); // 通过这个对象,获取密码.还有权限列表.         // 最终我们得把获取到的数据封装成UserDetails对象.交给spring security处理去.         List<GrantedAuthority> grantedAuthorityList = Collections.emptyList(); // 权限列表.         // 封装UserDetails对象.         //      User是UserDetails实现类.所以咱们可以直接返回这个实现类对象.         return new User(username, user.getPassword(), grantedAuthorityList);     } } ​
  1. 手动完成认证

    整个spring security一共15个过滤器. 其中有一个负责账号密码认证的过滤器: UsernamePasswordAuthenticationFilter.

需要一个认证管理器:

  • AutenticationManager, 咱们是配置类,将它注入到IoC容器当中.

@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {     return authenticationConfiguration.getAuthenticationManager(); }

直接调用认证方法:

@Override     public Result login(String username, String password) {         try {             // 1. 将用户名称和密码封装成UsernamePasswordAuthenticationToken.             UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =                     new UsernamePasswordAuthenticationToken(username, password); ​             // 2. 调用AuthenticationManager提供认证方法.             // Authentication authenticate(Authentication authentication) throws AuthenticationException;             Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken); ​             // 3. 存储认证结果.             SecurityContextHolder.getContext().setAuthentication(authenticate);             return Result.success("认证成功", authenticate);         }catch (AuthenticationException e){             return Result.error("认证失败", e.getMessage());         }     }

  1. 更改配置文件

    得去执行我们自己的认证页面.

@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {     return http.authorizeRequests(authorize ->                     authorize.mvcMatchers("/tom", "/login")                             .permitAll()                             .anyRequest()                             .authenticated())             // .formLogin() // 仅仅表示我使用表单验证, 但是配置用的都是默认的.             .formLogin(form -> form.loginPage("/login.html").permitAll()                     .successHandler(new AuthenticationSuccessHandler() {                         @Override                         public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {                             // 给前端返回一个json串.应用于前后端分离的项目.                             response.setContentType("application/json;charset=utf-8");                             Map<String, Object> map = new HashMap<>();                             map.put("code", 0); // 状态码                             map.put("msg", "认证成功");                             map.put("authentication", authentication); ​                             PrintWriter writer = response.getWriter();                             String json = new ObjectMapper().writeValueAsString(map);                             writer.print(json);                         }                     }).failureHandler(new AuthenticationFailureHandler() {                         @Override                         public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {                             // 给前端返回一个json串.应用于前后端分离的项目.                             response.setContentType("application/json;charset=utf-8");                             Map<String, Object> map = new HashMap<>();                             map.put("code", -1); // 状态码                             map.put("msg", "认证失败");                             map.put("exception", exception); ​                             PrintWriter writer = response.getWriter();                             String json = new ObjectMapper().writeValueAsString(map);                             writer.print(json);                         }                     })) ​             // .successForwardUrl("/success") // 默认的话,跳转到你在认证之前的请求.             // .defaultSuccessUrl("/success", true) // true,表示强制跳转到指定的url             .csrf(csrf -> csrf.disable())             .build(); }

2.4 jwt

<dependency>     <groupId>io.jsonwebtoken</groupId>     <artifactId>jjwt-api</artifactId>     <version>0.11.2</version> </dependency> <dependency>     <groupId>io.jsonwebtoken</groupId>     <artifactId>jjwt-impl</artifactId>     <version>0.11.2</version>     <scope>runtime</scope> </dependency> <dependency>     <groupId>io.jsonwebtoken</groupId>     <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->     <version>0.11.2</version>     <scope>runtime</scope> </dependency> ​ <!--解决高版本JDK问题--> <!--javax.xml.bind.DatatypeConverter错误--> <dependency>     <groupId>javax.xml.bind</groupId>     <artifactId>jaxb-api</artifactId>     <version>2.3.0</version> </dependency> <dependency>     <groupId>com.sun.xml.bind</groupId>     <artifactId>jaxb-impl</artifactId>     <version>2.3.0</version> </dependency> <dependency>     <groupId>com.sun.xml.bind</groupId>     <artifactId>jaxb-core</artifactId>     <version>2.3.0</version> </dependency> <dependency>     <groupId>javax.activation</groupId>     <artifactId>activation</artifactId>     <version>1.1.1</version> </dependency>

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

安全, 以json方式传输, 可以被验证和信任.本质还是一个字符串.定义规则,咱们可控的.


package com.tingyi.utils; ​ import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; ​ import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import java.security.Key; import java.util.Date; import java.util.UUID; ​ /**  * jwt工具类.  */ public class JwtUtil {     /**      * jwt过期时间      */     public static final Long EXP_TTL = 60 * 60 * 1000L; ​     /**      * jwt使用的密钥      */     public static final String JWT_KEY = "c3R1ZHkgaGFyZCBhbmQgbWFrZSBwcm9ncmVzcyBldmVyeSBkYXku"; ​     /**      * 创建jwt字符串      * @param id id      * @param issuer 创建的作者      * @param subject 用户主体      * @param ttlMillis 过期时间, 毫秒值      * @return jwt字符串      */     public static String createJWT(String id, String issuer, String subject, long ttlMillis) {         SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;         long nowMillis = System.currentTimeMillis();         Date now = new Date(nowMillis); ​         byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(JWT_KEY);         Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); ​         JwtBuilder builder = Jwts                 .builder()                 .setId(id)                 .setIssuedAt(now)                 .setSubject(subject)                 .setIssuer(issuer)                 .signWith(signingKey, signatureAlgorithm); ​         if (ttlMillis >= 0) {             long expMillis = nowMillis + ttlMillis;             Date exp = new Date(expMillis);             builder.setExpiration(exp);         }         return builder.compact();     } ​     /**      * 创建jwt字符串      * @param issuer 作者信息      * @param subject 用户主体信息      * @param ttlMillis 过期时间, 毫秒值      * @return jwt字符串      */     public static String createJwt(String issuer, String subject, long ttlMillis){         return createJWT(uuid(), issuer, subject, ttlMillis);     } ​     /**      * 创建jwt字符串      * @param issuer 作者信息      * @param subject 用户主体信息      * @return jwt字符串      */     public static String createJwt(String issuer, String subject){         return createJwt(issuer, subject, EXP_TTL);     } ​     /**      * 创建jwt字符串      * @param subject 用户主体      * @return jwt字符串      */     public static String createJwt(String subject){         return createJwt("laoren", subject, EXP_TTL);     } ​     /**      * uuid      * @return String      */     private static String uuid(){         return UUID.randomUUID().toString().replaceAll("-", "");     } ​     /**      * 解析jwt      * @param jwt  jwt字符串      * @return  Claims      */     public static Claims parseJWT(String jwt) {         Claims claims = Jwts.parserBuilder()                 .setSigningKey(DatatypeConverter.parseBase64Binary(JWT_KEY))                 .build()                 .parseClaimsJws(jwt).getBody(); ​         return claims;     } ​     public static void main(String[] args) {         // 生成一个jwt串.         String jwt = createJWT("1024", "tom", "jack", EXP_TTL);         System.out.println(jwt);         // 解析jwt串.         Claims claims = parseJWT(jwt);         Object subject = claims.get("subject");         System.out.println(subject);         System.out.println(claims);         System.out.println(claims.getSubject());         System.out.println(claims.getIssuedAt());         System.out.println(claims.getExpiration());     } }

2.5 官网认证流程

Form Login :: Spring Security

2.6 RBAC模型

RBAC(Role-Based Access Control),基于角色的访问控制。通过用户关联角色,角色关联权限,来间接的为用户赋予权限。

4.1. 创建表结构

下面是标准的RBAC模型关系表:

用户表

-- 用户表 CREATE TABLE `sys_user` (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',   `name` varchar(50) NOT NULL COMMENT '用户名',   `nick_name` varchar(150) DEFAULT NULL COMMENT '昵称',   `password` varchar(100) DEFAULT NULL COMMENT '密码',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   PRIMARY KEY (`id`),   UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8 COMMENT='用户管理';

角色表

-- 角色表 CREATE TABLE `sys_role` (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',   `name` varchar(100) DEFAULT NULL COMMENT '角色名称',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='角色管理';

用户角色表

-- 用户角色表 CREATE TABLE `sys_user_role` (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',   `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',   `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=88 DEFAULT CHARSET=utf8 COMMENT='用户角色';

菜单表

-- 菜单表 CREATE TABLE `sys_menu` (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',   `name` varchar(50) DEFAULT NULL COMMENT '菜单名称',   `parent_id` bigint(20) DEFAULT NULL COMMENT '父菜单ID,一级菜单为0',   `url` varchar(200) DEFAULT NULL COMMENT '菜单URL,类型:1.普通页面(如用户管理, /sys/user) 2.嵌套完整外部页面,以http(s)开头的链接 3.嵌套服务器页面,使用iframe:前缀+目标URL(如SQL监控, iframe:/druid/login.html, iframe:前缀会替换成服务器地址)',   `perms` varchar(500) DEFAULT NULL COMMENT '授权(多个用逗号分隔,如:sys:user:add,sys:user:edit)',   `type` int(11) DEFAULT NULL COMMENT '类型   0:目录   1:菜单   2:按钮',   `icon` varchar(50) DEFAULT NULL COMMENT '菜单图标',   `order_num` int(11) DEFAULT NULL COMMENT '排序',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=57 DEFAULT CHARSET=utf8 COMMENT='菜单管理';

角色菜单表

-- 角色菜单表 CREATE TABLE `sys_role_menu` (   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',   `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',   `menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',   `create_time` datetime DEFAULT NULL COMMENT '创建时间',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=623 DEFAULT CHARSET=utf8 COMMENT='角色菜单';

2.7 如何实现权限流程

按照: 认证的过程,其中实现了接口: UserDetailsService接口,之后,我们在重写的方法当中.

@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {     LambdaQueryWrapper<TbUser> queryWrapper = new LambdaQueryWrapper<>();     queryWrapper.eq(TbUser::getUsername, username);     TbUser user = tbUserMapper.selectOne(queryWrapper); // 通过这个对象,获取密码.还有权限列表.     // 最终我们得把获取到的数据封装成UserDetails对象.交给spring security处理去.     // 这个集合当中,包含两个东西     // 角色列表, 应该通过用户id去数据库当中,通过多表查询给它查询出来. List<Role>     // 权限列表, 通过用户id, 去数据库当中,通过多表查询,权限查出来. List<Menu>     List<GrantedAuthority> grantedAuthorityList = Collections.emptyList(); // 权限列表.          // 上一步完成之后,将封装好的List<GrantedAuthority>交给spring security,它会在我们需要验证权限的时候,就会给你验证了.     // 如何知道我需要进行权限验证,当类上或者方法上标记相关注解了.则表示我需要验证了.     // 当前登录的用户,是否有某个角色.     // 当前登录的用户, 是否拥有这个权限.     return new User(username, user.getPassword(), grantedAuthorityList); }

@Secured注解, 是否拥有某个角色,某些角色.

@PreAuthorize, 是否拥有某些权限.

 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!