当前位置: 首页 > news >正文

给你一个网站怎么做二级分销模式图

给你一个网站怎么做,二级分销模式图,包头市做网站公司,天津建设网站培训【SpringSecurity】springboot整合SpringSecurity实现登录校验与权限认证 【一】SpringSecurity框架简介【二】SpringSecurity与shiro【1】SpringSecurity特点【2】shiro特点【3】SpringSecurity和shiro总结 【三】SpringSecurity过滤器【1】SpringSecurity中常见的过滤器【2】… 【SpringSecurity】springboot整合SpringSecurity实现登录校验与权限认证 【一】SpringSecurity框架简介【二】SpringSecurity与shiro【1】SpringSecurity特点【2】shiro特点【3】SpringSecurity和shiro总结 【三】SpringSecurity过滤器【1】SpringSecurity中常见的过滤器【2】15种过滤器1WebAsyncManagerIntegrationFilter2SecurityContextPersistenceFilter3HeaderWriterFilter4CsrfFilter5LogoutFilter6UsernamePasswordAuthenticationFilter7DefaultLoginPageGeneratingFilter8DefaultLogoutPageGeneratingFilter9BasicAuthenticationFilter10RequestCacheAwareFilter11SecurityContextHolderAwareRequestFilter12AnonymousAuthenticationFilter13SessionManagementFilter14ExceptionTranslationFilter15FilterSecurityInterceptor 【3】SpringSecurity基本流程1整体流程概述2详细流程步骤 【四】PasswordEncoder接口【五】整合SpringSecurity实现登录校验与权限认证【1】身份认证1controller测试2登录认证流程3创建一个用户表4创建一个MyUserDetailsService类5通过配置类对AuthenticationManager与自定义的UserDetails和PasswordEncoder进行关联6在登录方法所在的类中注入AuthenticationManager7介绍UsernamePasswordAuthenticationToken8测试登录9SecurityFilterChain 过滤器10测试登录11测试退出登录 【2】权限校验1角色与权限2角色表与权限表3权限认证流程4在MyUserDetailsService中实现用户权限的赋值5MySysUserDetails中完成角色和权限的赋值6基于请求7基于方法 【3】总结 【五】Springboot整合SpringSecurity入门【1】pom.xml【2】application.properties【3】SecurityConfig【4】启动类【5】User实体类【6】UserService服务层【7】UserMapper【8】UserController控制层 【六】微服务认证与授权实现思路【七】微服务代码实例【1】父工程pom.xml【2】common模块【3】common模块-SpringSecurity子模块【4】common模块-service_base【5】gateway模块 【八】ruoyi平台整合SpringSecurity案例【待完成】 【一】SpringSecurity框架简介 关于安全方面的两个主要区域是“认证”和“授权”或者说是访问控制一般来说Web应用的安全性包括用户认证Authentication和用户授权Authorization两个部分这两点也是SpringSecurity重要核心功能。 1用户认证指的是验证某个用户是否为系统中的合法主体也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录。 2用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中不同用户所具有的的权限是不同的。比如对一个文件来说有的用户只能进行读取而有的用户可以进行修改。一般来说系统会为不同的用户分配不同的角色而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。 【二】SpringSecurity与shiro 【1】SpringSecurity特点 1与Spring框架无缝整合 2全面的权限控制 3专门为Web开发而设计 旧版本不能脱离Web环境使用 新版本对整个框架进行了分层抽取分成了核心模块和Web模块单独引入了核心模块就可以脱离Web环境 重量级 【2】shiro特点 Apache旗下的轻量级权限控制框架 1轻量级 shiro主张的理念是把复杂的事情变简单针对性能更高要求的互联网应用有更好的变现 2通用性 好处不局限于Web环境可以脱离Web环境使用 缺陷在Web环境下一些特定的需求需要手动编写代码定制 【3】SpringSecurity和shiro总结 相对于shiro在SSM中整合SpringSecurity都是比较麻烦的操作所以SpringSecurity虽然功能比shiro强大但是使用反而没有shiro多shiro虽然功能没有SpringSecurity多但是对于大部分项目而言shiro也够用了。自从有了Springboot之后Springboot对于SpringSecurity提供了自动化配置方案可以使用更少的配置来使用SpringSecurity。因此一般来说常见的安全管理技术栈的组合是这样的 1SSMshiro 2Springboot/SpringCloudSpringSecurity 以上只是一个推荐的组合而已如果单从技术上来说无论怎么组合都是可以运行的。 【三】SpringSecurity过滤器 【1】SpringSecurity中常见的过滤器 【2】15种过滤器 SpringSecurity采用的是责任链的设计模式它有一条很长的过滤器链。这些过滤器按特定顺序执行每个过滤器负责不同的安全任务如身份验证、授权、会话管理等。下面介绍一些重要的过滤器及其功能 1WebAsyncManagerIntegrationFilter 功能将 Spring Security 上下文与 Spring 的 WebAsyncManager 集成确保异步请求也能正确处理安全上下文。 位置通常位于过滤器链的最前端。 2SecurityContextPersistenceFilter 功能在每个请求开始时从 HttpSession 中获取安全上下文SecurityContext并将其设置到当前线程中在请求结束时将安全上下文保存回 HttpSession 中。 位置在请求处理的早期阶段执行。 3HeaderWriterFilter 功能用于向响应头中添加安全相关的头部信息如 X-Frame-Options、X-XSS-Protection 等增强应用的安全性。 位置在安全上下文设置之后执行。 4CsrfFilter 功能防止跨站请求伪造CSRF攻击验证请求中的 CSRF 令牌是否有效。 位置在处理表单提交等敏感请求之前执行。 5LogoutFilter 功能处理用户的注销请求清除安全上下文、销毁会话等。 位置在处理注销相关的 URL 请求时执行。 6UsernamePasswordAuthenticationFilter 功能处理基于表单的用户名和密码认证从请求中提取用户名和密码尝试进行身份验证。 位置通常在处理登录表单提交的 URL 时执行。 7DefaultLoginPageGeneratingFilter 功能如果没有自定义登录页面该过滤器会生成一个默认的登录页面。 位置在处理登录相关请求时若没有自定义登录页面则会起作用。 8DefaultLogoutPageGeneratingFilter 功能如果没有自定义注销页面该过滤器会生成一个默认的注销页面。 位置在处理注销相关请求时若没有自定义注销页面则会起作用。 9BasicAuthenticationFilter 功能处理基于 HTTP Basic 认证的请求从请求头中提取基本认证信息进行身份验证。 位置在处理需要 Basic 认证的请求时执行。 10RequestCacheAwareFilter 功能处理请求缓存当用户在未认证的情况下访问受保护资源时会缓存该请求认证成功后重定向到原请求的资源。 位置在认证前后处理请求缓存相关操作。 11SecurityContextHolderAwareRequestFilter 功能将 HttpServletRequest 包装成 SecurityContextHolderAwareRequestWrapper提供额外的安全相关方法。 位置在请求处理过程中对请求进行包装。 12AnonymousAuthenticationFilter 功能如果请求在经过前面的过滤器后仍未认证该过滤器会为请求设置一个匿名身份避免后续处理因缺少身份信息而出错。 位置在前面的认证过滤器之后执行。 13SessionManagementFilter 功能管理用户会话处理会话超时、并发会话控制等问题。 位置在会话相关操作的处理阶段执行。 14ExceptionTranslationFilter 功能捕获认证和授权过程中抛出的异常并将其转换为合适的 HTTP 响应如重定向到登录页面或返回 403 状态码。 位置在认证和授权过滤器之后处理异常情况。 15FilterSecurityInterceptor 功能进行最终的授权检查根据配置的访问规则判断用户是否有权限访问请求的资源。 位置位于过滤器链的末尾在所有其他过滤器执行完毕后进行最终的授权决策。 【3】SpringSecurity基本流程 1整体流程概述 当一个客户端发起请求时请求会进入 Spring Security 的过滤器链。过滤器链中的各个过滤器会依次对请求进行处理其中涉及认证的过滤器会尝试对用户进行身份验证。如果认证成功用户的身份信息会被存储在安全上下文中如果认证失败则会根据配置进行相应的错误处理。 2详细流程步骤 1请求进入过滤器链 客户端发起请求后请求首先会到达 Spring Security 的过滤器链。Spring Security 默认有多个过滤器这些过滤器按特定顺序排列每个过滤器负责不同的安全任务。例如SecurityContextPersistenceFilter 是过滤器链中的第一个过滤器它会在请求开始时从 HttpSession 中获取安全上下文SecurityContext并将其设置到当前线程中在请求结束时将安全上下文保存回 HttpSession 中。 2认证过滤器处理 不同类型的认证方式由不同的认证过滤器处理以下是几种常见的认证方式及其对应的过滤器 1-表单登录认证UsernamePasswordAuthenticationFilter 请求匹配当请求的 URL 匹配到配置的登录 URL默认为 /login且请求方法为 POST 时UsernamePasswordAuthenticationFilter 会开始工作。 提取认证信息该过滤器会从请求中提取用户名和密码通常是从表单的 username 和 password 字段中获取。 创建认证令牌使用提取的用户名和密码创建一个 UsernamePasswordAuthenticationToken 对象该对象实现了 Authentication 接口用于封装用户的认证信息。 调用认证管理器将创建的 UsernamePasswordAuthenticationToken 对象传递给 AuthenticationManager 进行认证。 2-HTTP Basic 认证BasicAuthenticationFilter 请求匹配当请求头中包含 Authorization 字段且值以 Basic 开头时BasicAuthenticationFilter 会对请求进行处理。 提取认证信息从 Authorization 字段中提取基本认证信息通常是经过 Base64 编码的用户名和密码并进行解码。 创建认证令牌使用解码后的用户名和密码创建 UsernamePasswordAuthenticationToken 对象。 调用认证管理器将认证令牌传递给 AuthenticationManager 进行认证。 3认证管理器AuthenticationManager处理 AuthenticationManager 是一个接口它的主要职责是对传入的 Authentication 对象进行认证。默认实现是 ProviderManager它内部维护了一个 AuthenticationProvider 列表。 1-遍历认证提供者ProviderManager 会遍历 AuthenticationProvider 列表依次调用每个 AuthenticationProvider 的 authenticate 方法直到找到能够处理该 Authentication 对象的 AuthenticationProvider。 2-认证处理AuthenticationProvider 会根据具体的认证逻辑对 Authentication 对象进行验证例如查询数据库验证用户名和密码是否匹配。如果验证成功会返回一个已认证的 Authentication 对象如果验证失败会抛出相应的异常。 4用户详情服务UserDetailsService 在认证过程中AuthenticationProvider 通常会调用 UserDetailsService 来获取用户的详细信息。UserDetailsService 是一个接口其主要方法是 loadUserByUsername该方法根据用户名从数据源如数据库、LDAP 等中加载用户的详细信息返回一个 UserDetails 对象。UserDetails 接口封装了用户的核心信息如用户名、密码、权限等。 5认证结果处理 认证成功如果 AuthenticationProvider 认证成功会返回一个已认证的 Authentication 对象其中包含用户的详细信息和权限。UsernamePasswordAuthenticationFilter 或 BasicAuthenticationFilter 会将该对象设置到安全上下文中SecurityContextHolder表示用户已成功认证。 认证失败如果认证失败AuthenticationProvider 会抛出相应的异常如 BadCredentialsException用户名或密码错误。ExceptionTranslationFilter 会捕获这些异常并根据配置进行相应的处理例如重定向到登录页面或返回 401 未授权状态码。 6后续请求处理 认证成功后用户的身份信息会存储在安全上下文中。后续的请求会通过 SecurityContextPersistenceFilter 从 HttpSession 中恢复安全上下文确保用户在整个会话期间保持认证状态。同时FilterSecurityInterceptor 会根据配置的访问规则对请求进行授权检查判断用户是否有权限访问请求的资源。 【四】PasswordEncoder接口 【五】整合SpringSecurity实现登录校验与权限认证 创建一个spring boot项目并导入一些初始依赖不赘述 【1】身份认证 1controller测试 由于我们加入了 spring-boot-starter-security 的依赖所以security就会自动生效了。这时直接编写一个controller控制器并编写一个接口进行测试 可以看到我们在访问这个接口时出现了拦截必须要我们进行登录之后才能访问 2登录认证流程 Spring Security 6.x 的认证实现流程如下 1用户提交登录请求 2Spring Security 将请求交给 UsernamePasswordAuthenticationFilter 过滤器处理。 3UsernamePasswordAuthenticationFilter 获取请求中的用户名和密码并生成一个 AuthenticationToken 对象将其交给 AuthenticationManager 进行认证。 4AuthenticationManager 通过 UserDetailsService 获取用户信息然后使用 PasswordEncoder 对用户密码进行校验。 5如果密码正确AuthenticationManager 会生成一个认证通过的 Authentication 对象并返回给 UsernamePasswordAuthenticationFilter 过滤器。如果密码不正确则 AuthenticationManager 抛出一个 AuthenticationException 异常。 6UsernamePasswordAuthenticationFilter 将 Authentication 对象交给 SecurityContextHolder 进行管理并调用 AuthenticationSuccessHandler 处理认证成功的情况。 7如果认证失败UsernamePasswordAuthenticationFilter 会调用 AuthenticationFailureHandler 处理认证失败的情况。 看起来有点复杂其实写起来很简单的。spring security的底层就是一堆的过滤器来是实现的而我们只需要编写一些重要的过滤器即可其他的就用spring security默认的实现只要不影响我们正常的登录功能即可。 3创建一个用户表 创建一个用户表用来进行登录实现注意这个表中的用户名不能重复我们将用户名作为每一个用户的唯一凭证就如同人的手机号或者身份证号一样。 实体类、mapper、service、controller等基本配置不赘述 4创建一个MyUserDetailsService类 创建一个MyUserDetailsService类来实现SpringSecurity的UserDetailsService接口这里进行用户登录和授权的逻辑处理 UserDetailsService此接口中定义了登录服务方法用来实现登录逻辑。方法的返回值是UserDetails也是spring security框架定义中的一个接口用来存储用户信息我们可以自定义一个类用来实现这个接口将来返回的时候就返回我们自定义的用户实体类。 1实现UserDetailsService接口 Component public class MyUserDetailsService implements UserDetailsService {/** UserDetailsService提供查询用户功能如根据用户名查询用户并返回UserDetails*UserDetailsSpringSecurity定义的类 记录用户信息如用户名、密码、权限等* */Autowiredprivate SysUserMapper sysUserMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名从数据库中查询用户SysUser sysUser sysUserMapper.selectOne(new LambdaQueryWrapperSysUser().eq(username ! null, SysUser::getUsername, username));if (sysUsernull){throw new UsernameNotFoundException(用户不存在);}// 封装查询到的用户信息MySysUserDetails mySysUserDetailsnew MySysUserDetails(sysUser);return mySysUserDetails;} } 2实现UserDetails接口 Data AllArgsConstructor NoArgsConstructor public class MySysUserDetails implements UserDetails {private Integer id;private String username;private String password;// 用户拥有的权限集合我这里先设置为null将来会再更改的Overridepublic Collection? extends GrantedAuthority getAuthorities() {return null;}public MySysUserDetails(SysUser sysUser) {this.id sysUser.getId();this.username sysUser.getUsername();this.password sysUser.getPassword();}// 后面四个方法都是用户是否可用、是否过期之类的。我都设置为trueOverridepublic boolean isAccountNonExpired() {return true;}Overridepublic boolean isAccountNonLocked() {return true;}Overridepublic boolean isCredentialsNonExpired() {return true;}Overridepublic boolean isEnabled() {return true;} }5通过配置类对AuthenticationManager与自定义的UserDetails和PasswordEncoder进行关联 Spring Security是通过AuthenticationManager实现的认证会借此来判断用户名和密码的正确性 密码解析器spring security框架定义的接口PasswordEncoder spring security框架强制要求必须在spring容器中存在PasswordEncoder类型对象且对象唯一 Configuration EnableWebSecurity //开启webSecurity服务 public class SecurityConfig {Autowiredprivate MyUserDetailsService myUserDetailsService;Beanpublic AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){DaoAuthenticationProvider providernew DaoAuthenticationProvider();//将编写的UserDetailsService注入进来provider.setUserDetailsService(myUserDetailsService);//将使用的密码编译器加入进来provider.setPasswordEncoder(passwordEncoder);//将provider放置到AuthenticationManager 中ProviderManager providerManagernew ProviderManager(provider);return providerManager;}/** 在security安全框架中提供了若干密码解析器实现类型。* 其中BCryptPasswordEncoder 叫强散列加密。可以保证相同的明文多次加密后* 密码有相同的散列数据而不是相同的结果。* 匹配时是基于相同的散列数据做的匹配。* Spring Security 推荐使用 BCryptPasswordEncoder 作为密码加密和解析器。* */ Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();} }6在登录方法所在的类中注入AuthenticationManager 调用authenticate实现认证逻辑并且在认证之后返回认证过的用户信息 1controller层 // 用户登录 PostMapping(/login) public String login(RequestBody LoginDto loginDto){String token sysUserService.login(loginDto);return token; }2对应的service层的方法 编写具体的登录方法创建一个UsernamePasswordAuthenticationToken对象并传入相应的用户名和密码注入一个AuthenticationManager的bean这个bean是spring security封装的用来进行认证的类调用这个类的authenticate方法并传入UsernamePasswordAuthenticationToken对象 Autowiredprivate AuthenticationManager authenticationManager;// 登录接口的具体实现Overridepublic String login(LoginDto loginDto) {// 传入用户名和密码UsernamePasswordAuthenticationToken usernamePassword new UsernamePasswordAuthenticationToken(loginDto.getUsername(),loginDto.getPassword());//是实现登录逻辑此时就会去调用LoadUserByUsername方法Authentication authenticate authenticationManager.authenticate(usernamePassword);//获取返回的用户信息Object principal authenticate.getPrincipal();//强转为MySysUserDetails类型MySysUserDetails mySysUserDetails (MySysUserDetails) principal;//输出用户信息System.err.println(mySysUserDetails);//返回tokenString token UUID.randomUUID().toString();return token;}7介绍UsernamePasswordAuthenticationToken UsernamePasswordAuthenticationToken是Spring Security中用于表示基于用户名和密码的身份验证令牌的类。它主要有以下两个构造方法 1UsernamePasswordAuthenticationToken(Object principal, Object credentials) 1-principal参数表示认证主体通常是用户名或用户对象。在身份验证过程中这通常是用来标识用户的信息可以是用户名、邮箱等。 2-credentials参数表示凭据通常是用户的密码或其他凭证信息。在身份验证过程中这用于验证用户的身份。 2UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection? extends GrantedAuthority authorities) 1-除了上述两个参数外这个构造方法还接受一个授权权限集合authorities参数。这个集合表示用户所拥有的权限通常是一个包含用户权限信息的集合。 2-GrantedAuthority接口代表了用户的权限信息可以通过该接口的实现类来表示用户具体的权限。 这两个构造方法的作用是创建一个包含用户身份信息、凭据信息和权限信息的身份验证令牌以便在Spring Security中进行身份验证和授权操作。通过这些构造方法可以将用户的相关信息封装成一个完整的身份验证对象方便在安全框架中进行处理和验证。 总之UsernamePasswordAuthenticationToken是在Spring Security中用于表示用户名密码身份验证信息的重要类通过不同的构造方法可以满足不同场景下的需求。 8测试登录 造一些用户数据并进行测试 访问访问http://localhost:8080/test自动跳转到了Spring Security提供的默认的登录页面这是因为Spring Security默认所有的请求都要先登录才行我们在这里登录之后就可以继续访问test页面了 这里的用户名和密码就是我们在数据库中存储的用户名和密码 既然这个test请求要先进行拦截认证才能访问那么我们刚才编写的登录接口sys-user/login岂不是也要先进行拦截认证才能访问这就与我们编写登录接口的初衷违背了我们这个接口就是用来登陆的现在还要先登录认证之后再访问这个登录接口。那么有没有一种方法不使用SpringSecurity默认的登录页面呢使我们编写的登录接口所有人都可以直接访问呢 9SecurityFilterChain 过滤器 配置用户登录的接口可以暴露出来被所有人都正常的访问不会被拦截转跳到默认登录页面而是跳到自定义的登录页面。 在第二步设置的SecurityConfig类中设置过滤器 1在spring security6.x版本之后原先经常用的and()方法被废除了现在spring官方推荐使用Lambda表达式的写法。 2因为我们接下来要进行测试所以禁用CSRF保护 /** 配置权限相关的配置* 安全框架本质上是一堆的过滤器称之为过滤器链每一个过滤器链的功能都不同* 设置一些链接不要拦截* */Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {//关闭csrfhttpSecurity.csrf(it-it.disable()); httpSecurity.authorizeHttpRequests(it-it.requestMatchers(/login,/sys-user/login).permitAll() //设置登录路径所有人都可以访问.anyRequest().authenticated() //其他路径都要进行拦截);//表单httpSecurity.formLogin(from-from.loginPage(/login) //跳转到自定义的登录页面.loginProcessingUrl(/sys-user/login) //处理前端的请求与from表单的action一致即可.defaultSuccessUrl(/index) //默认的请求成功之后的跳转页面直接访问登录页面);return httpSecurity.build();} 配置对应的controller Controller public class Login {GetMapping(/login)public String login(){System.out.println(用户进入登录页面);return login; //没使用json返回直接映射到自定义登录的页面}GetMapping(/index)ResponseBodypublic String index(){return 用户登录成功;} }10测试登录 1访问test请求遇到拦截说明我们的配置生效了 2访问login请求并用账号密码登陆成功 登录之后会跳转到/test请求地址 现在我们直接访问/login登录页面可以看到返回了/index页面的内容这个是我们设置的默认登录成功之后返回的页面 11测试退出登录 需要注意的是在Spring Security中没有专门用于处理退出失败的接口。退出注销操作通常是由浏览器发起的Spring Security会拦截注销请求并执行相应的注销逻辑。 退出操作通常是通过调用SecurityContextLogoutHandler来完成的它会清除用户的安全上下文包括认证信息和会话信息。 在security框架中默认提供了退出登陆的功能。请求地址是 /lohout 此为默认值可以通过配置进行修该。直接请求 /logout 会实现自动退出登录逻辑默认的/logout接收get、和post请求 退出登陆时会清楚内存中的登录用户主体信息销毁会话对象等等。 自定义退出接口 httpSecurity.logout(logout-{logout.logoutUrl(/user/login) //自定义退出接口.logoutSuccessHandler(logoutSuccess); //退出成功之后的逻辑});编写退出成功之后的逻辑我们可以在这里删除掉redis中的数据清除登录的上下文设置返回的信息等等…如果是前后端分离状态下的spring security这些工作都可以在自定义的退出接口中进行实现。如果是前后端不分离的表单式登录还是使用传统的Cookie和Session来进行用户信息的保存我们自需要调用ogout.logoutUrl(“/user/login”) 方法来指定退出路径即可退出的逻辑不需要我们来实现。 Component public class LogoutSuccess implements LogoutSuccessHandler {Resourceprivate RedisTemplateString,String redisTemplate;/* * 登录成功之后的逻辑 * */Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String token request.getHeader(token);// 删除redis中的数据redisTemplate.delete(token);MapString,Object mapnew HashMap();map.put(msg,退出成功);map.put(code,200);response.getWriter().write(JSON.toJSONString(map));response.setContentType(application/json;charsetutf-8);}}【2】权限校验 1角色与权限 角色与权限在SpringSecurity中的作用 1角色(Role) 角色是一组权限的集合通常代表着用户的身份或职责。在Spring Security中可以通过配置将角色分配给用户或者用户组以此来控制用户对系统资源的访问。例如管理员拥有添加、删除和修改用户的权限而普通用户只能查看自己的信息。 2权限(Permission) 权限是指对某一特定资源的访问控制例如读写文件、访问数据库等。在Spring Security中通常使用“资源-操作”命名方式来定义权限例如“/admin/* - GET”表示允许访问以/admin/开头的所有URL的GET请求。可以将权限分配给角色也可以将其分配给单独的用户。 2角色表与权限表 角色与权限之间的关系是多对多的建立两张简单的表一张用来存放角色、一张用来存放权限 1角色表 2权限表 其他代码自动生成不赘述 3权限认证流程 SpringSecurity要求将身份认证信息存到GrantedAuthority对象列表中。代表了当前用户的权限。 GrantedAuthority对象由AuthenticationManager插入到Authentication对象中然后在做出授权决策 时由AccessDecisionManager实例读取。 GrantedAuthority 接口只有一个方法 String getAuthority();AuthorizationManager实例通过该方法来获得GrantedAuthority。通过字符串的形式表示 GrantedAuthority可以很容易地被大多数AuthorizationManager实现读取。如果GrantedAuthority不 能精确地表示为String则GrantedAuthorization被认为是复杂的getAuthority()必须返回null 直接在登录时查询用户的权限并放在我们自定义的实现了UserDetail的接口类中用来表示登录用户的全部信息 4在MyUserDetailsService中实现用户权限的赋值 在MySysUserDetails类中加入两个属性记录从数据库中查处的角色和权限信息 这里就简单一点不在做多表关联查询了。直接把zhangsan用户设置为超级管理员拥有所有权限lisi用户设置为普通管理员拥有基本权限。 在MyUserDetailsService中实现用户权限的赋值 Component public class MyUserDetailsService implements UserDetailsService {/** UserDetailsService提供查询用户功能如根据用户名查询用户并返回UserDetails*UserDetailsSpringSecurity定义的类 记录用户信息如用户名、密码、权限等* */Autowiredprivate SysUserMapper sysUserMapper;Autowiredprivate SysRoleMapper sysRoleMapper;Autowiredprivate SysPermissionsMapper sysPermissionsMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名从数据库中查询用户SysUser sysUser sysUserMapper.selectOne(new LambdaQueryWrapperSysUser().eq(username ! null, SysUser::getUsername, username));if (sysUsernull){throw new UsernameNotFoundException(用户不存在);}MySysUserDetails mySysUserDetailsnew MySysUserDetails(sysUser);if (zhangsan.equals(username)){//zhangsan用户是超级管理员拥有一切权限SysRole sysRole sysRoleMapper.selectOne(new LambdaQueryWrapperSysRole().eq(SysRole::getRoleName, 超级管理员));SetSysRole rolesnew HashSet();roles.add(sysRole);mySysUserDetails.setRoles(roles);SysPermissions sysPermissions sysPermissionsMapper.selectById(1);SetString permissionsnew HashSet();permissions.add(sysPermissions.getPermissionsName());mySysUserDetails.setPermissions(permissions);}if (lisi.equals(username)){ //lisi用户是普通管理员拥有基本权限SysRole sysRole sysRoleMapper.selectOne(new LambdaQueryWrapperSysRole().eq(SysRole::getRoleName, 普通管理员));SetSysRole rolesnew HashSet();roles.add(sysRole);mySysUserDetails.setRoles(roles);SysPermissions sysPermissions sysPermissionsMapper.selectById(2);SetString permissionsnew HashSet();permissions.add(sysPermissions.getPermissionsName());mySysUserDetails.setPermissions(permissions);}return mySysUserDetails;} } 5MySysUserDetails中完成角色和权限的赋值 private SetSysRole roles;// 权限信息private SetString permissions;// 用户拥有的权限集合我这里先设置为null将来会再更改的Overridepublic Collection? extends GrantedAuthority getAuthorities() {System.err.println(进入权限的获取方法);ListGrantedAuthority authorities new ArrayList(); // 授权信息列表// 将角色名称添加到授权信息列表中roles.forEach(role-authorities.add(new SimpleGrantedAuthority(role.getRoleName())));// 将权限名称添加到授权信息列表中permissions.forEach(permission-authorities.add(new SimpleGrantedAuthority(permission)));return authorities; // 返回授权信息列表}用户认证之后会去存储用户对应的权限并且给资源设置对应的权限SpringSecurity支持两种粒度 的权限 1、基于请求的在配置文件中配置路径可以使用**的通配符 2、基于方法的在方法上使用注解实现 角色配置在UserDetails接口中存在相关的权限和角色管理只不过我们在实现这个接口的时候将这些都设置为了null。现在我们只需要将这些信息实现即可 6基于请求 还是在SecurityFilter过滤器中实现请求地址的权限校验 httpSecurity.authorizeHttpRequests(it- //hello地址只有超级管理员角色才能访问 it.requestMatchers(/hello).hasRole(超级管理员) //hello2地址只有拥有所有权限的权限才能访问 .requestMatchers(hello2).hasAuthority(拥有所有权限).requestMatchers(/login,sys-user/login).permitAll() //设置登录路径所有人都可以访问.anyRequest().authenticated() //其他路径都要进行拦截);使用sili进行登录时访问hello2接口显示权限不够 使用zhangsan进行登录时访问hello2接口可以访问到 7基于方法 基于方法的权限认证要在SecurityConfig类上加上EnableMethodSecurity注解表示开启了方法权限的使用 常用的有四个注解 PreAuthorize PostAuthorize PreFilter PostFilter /*测试PreAuthorize注解 * 作用使用在类或方法上拥有指定的权限才能访问在方法运行前进行校验 * String类型的参数语法是Spring的EL表达式 * 有权限test3权限 * hasRole会去匹配authorities但是会在hasRole的参数前加上一个ROLE_前缀 * 所以在定义权限的时候需要加上ROLE_前缀 * role和authorities的关系是role是一种复杂的写法有ROLE_前缀authorities是role的简化写法 * 如果使用 * hasAnyRole则匹配的权限是在authorities加上前缀ROLE_ * 推荐使用 * hasAnyAuthority匹配authorities但是不用在authorities的参数前加上ROLE_前缀 * */ PreAuthorize(hasAnyAuthority(拥有所有权限)) ResponseBody GetMapping(/test3) public String test3(){System.out.println(一个请求);return 一个test3请求; }/*PostAuthorize在方法返回时进行校验。可以还是校验权限、或者校验一些其他的东西接下来我们校验返回值的长度 *返回结果的长度大于3、则认为是合法的 returnObject固定写法代指返回对象 * */ ResponseBody PostAuthorize(returnObject.length()4) GetMapping(/test4) public String test4(){System.out.println(一个test4请求);return 小张自傲张最终; }/* * PreFilter过滤符合条件的数据进入到接口 * */PostFilter(filterObject.length()3)ResponseBodyGetMapping(/test5)public String test5(){System.out.println(一个test4请求);ListString list new ArrayList();list.add(张三);list.add(王麻子);list.add(狗叫什么);return 一个test5请求;}/* * PreFilter过滤符合条件的数据返回数据必须是Collection、map、Array【数组】 * */ PreFilter(filterObject.length()5) ResponseBody PostMapping(/test6) public ListString test6(RequestBody ListString list){return list; }需要注意的是这些方法不仅仅局限在权限的校验还能对返回的结果做一定的操作 最需要注意的就是PreFilter注解它要求前端传递的参数一定是数组或集合 基于方法鉴权 在SpringSecurity6版本中EnableGlobalMethodSecurity被弃用取而代之的是 EnableMethodSecurity。默认情况下会激活pre-post注解并在内部使用 AuthorizationManager。 新老API区别 此EnableMethodSecurity替代了EnableGlobalMethodSecurity。提供了以下改进 使用简化的AuthorizationManager。支持直接基于bean的配置而不需要扩展GlobalMethodSecurityConfiguration使用Spring AOP构建删除抽象并允许您使用Spring AOP构建块进行自定义检查是否存在冲突的注释以确保明确的安全配置符合JSR-250默认情况下启用PreAuthorize、PostAuthorize、PreFilter和PostFilter 【3】总结 1登录校验Authentication 1-用户提交用户名和密码进行登录。 2-Spring Security会拦截登录请求并将用户名和密码与存储在系统中的凭据如数据库或LDAP进行比对。 3-如果用户名和密码匹配则认为用户通过了身份验证可以继续访问受限资源。 4-认证成功后Spring Security会创建一个包含用户信息和权限的安全上下文Security Context。 2权限认证Authorization 1-一旦用户通过了身份验证Spring Security就会开始进行权限认证。 2-针对每个受限资源或操作可以配置相应的权限要求例如需要哪些角色或权限才能访问。 3-Spring Security会根据配置的权限要求检查当前用户所拥有的角色和权限判断是否满足访问条件。 4-如果用户拥有足够的角色或权限就被允许访问资源否则将被拒绝访问并可能重定向到登录页面或返回相应的错误信息。 Spring Security通过身份验证Authentication来确认用户的身份并通过授权Authorization来控制用户对受保护资源的访问。这种分离的设计使得安全配置更加灵活并且可以轻松地对不同的用户和角色进行管理和控制。 【五】Springboot整合SpringSecurity入门 【1】pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.2.1.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.guor/groupIdartifactIdsecurityProject/artifactIdversion0.0.1-SNAPSHOT/versionnamesecurityProject/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--mybatis-plus--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.0.5/version/dependency!--mysql--dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependency!--lombok用来简化实体类--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project【2】application.properties server.port8111 #spring.security.user.nameroot #spring.security.user.passwordroot#mysql数据库连接 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.urljdbc:mysql://localhost:3306/security?serverTimezoneGMT%2B8 spring.datasource.usernameroot spring.datasource.passwordroot【3】SecurityConfig SecurityConfig package com.guor.security.config;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.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;public class SecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder();String password passwordEncoder.encode(123);auth.inMemoryAuthentication().withUser(zs).password(password).roles(admin);}BeanPasswordEncoder password() {return new BCryptPasswordEncoder();} }UserSecurityConfig package com.guor.security.config;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.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.csrf.CookieCsrfTokenRepository;import javax.sql.DataSource;Configuration public class UserSecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate UserService userService;//注入数据源Autowiredprivate DataSource dataSource;//配置对象Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl jdbcTokenRepository new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;}Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userService(userService).passwordEncoder(password());}BeanPasswordEncoder password() {return new BCryptPasswordEncoder();}Overrideprotected void configure(HttpSecurity http) throws Exception {//退出http.logout().logoutUrl(/logout).logoutSuccessUrl(/test/hello).permitAll();//配置没有权限访问跳转自定义页面http.exceptionHandling().accessDeniedPage(/unauth.html);http.formLogin() //自定义自己编写的登录页面.loginPage(/on.html) //登录页面设置.loginProcessingUrl(/user/login) //登录访问路径.defaultSuccessUrl(/success.html).permitAll() //登录成功之后跳转路径.failureUrl(/unauth.html).and().authorizeRequests().antMatchers(/,/test/hello,/user/login).permitAll() //设置哪些路径可以直接访问不需要认证//当前登录用户只有具有admins权限才可以访问这个路径//1 hasAuthority方法// .antMatchers(/test/index).hasAuthority(admins)//2 hasAnyAuthority方法// .antMatchers(/test/index).hasAnyAuthority(admins,manager)//3 hasRole方法 ROLE_sale.antMatchers(/test/index).hasRole(sale).anyRequest().authenticated().and().rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(60)//设置有效时长单位秒.userDetailsService(userService);// .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());// .and().csrf().disable(); //关闭csrf防护} }【4】启动类 【5】User实体类 package com.guor.security.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data public class User {private Integer id;private String username;private String password; }【6】UserService服务层 package com.guor.security.service;import com.guor.security.entity.User; import com.guor.security.mapper.UsersMapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; 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.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service;import java.util.List;Service public class UserServiceImpl implements UserService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//调用usersMapper方法根据用户名查询数据库QueryWrapperUsers wrapper new QueryWrapper();// where username?wrapper.eq(username,username);User user userMapper.selectOne(wrapper);//判断if(user null) {//数据库没有用户名认证失败throw new UsernameNotFoundException(用户名不存在);}ListGrantedAuthority auths AuthorityUtils.commaSeparatedStringToAuthorityList(admin,ROLE_sale);//从查询数据库返回users对象得到用户名和密码返回return new User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),auths);} }【7】UserMapper package com.guor.security.mapper;import com.guor.security.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository;Repository public interface UserMapper extends BaseMapperUser { }【8】UserController控制层 package com.guor.security.controller;import com.guor.security.entity.User; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PostFilter; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList; import java.util.List;RestController RequestMapping(/test) public class UserController {GetMapping(hello)public String hello() {return hello security;}GetMapping(index)public String index() {return hello index;}GetMapping(update)//Secured({ROLE_sale,ROLE_manager})//PreAuthorize(hasAnyAuthority(admins))PostAuthorize(hasAnyAuthority(admins))public String update() {System.out.println(update......);return hello update;}GetMapping(getAll)PostAuthorize(hasAnyAuthority(admins))PostFilter(filterObject.username admin1)public ListUsers getAllUser(){ArrayListUsers list new ArrayList();list.add(new Users(11,admin1,6666));list.add(new Users(21,admin2,888));System.out.println(list);return list;} }【六】微服务认证与授权实现思路 1如果是基于Session那么SpringSecurity会对cookie里的SessionID进行解析找到服务器存储的Session信息然后判断当前用户是否复合请求的要求 2如果是token则是解析出token然后将当前请求加入到SpringSecurity管理的权限信息中去 如果系统的模块众多每个模块都需要进行授权与认证所以我们选择基于token的形式进行授权与认证用户根据用户名密码认证成功然后获取当前用户角色的一系列权限值并以用户名为key权限列表为value的形式存入redis缓存中根据用户名相关信息生成token返回浏览器将token记录到cookie中每次调用api接口都默认将token携带到header请求头中SpringSecurity解析header头获取token信息解析token获取当前用户名根据用户名就可以从redis中获取权限列表这样SpringSecurity就能够判断当前请求是否有权限访问。 【七】微服务代码实例 【1】父工程pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionmodulesmodulecommon/modulemoduleinfrastructure/modulemoduleservice/module/modulesparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.2.1.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.atguigu/groupIdartifactIdacl_parent/artifactIdpackagingpom/packagingversion0.0.1-SNAPSHOT/versionnameacl_parent/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.versionmybatis-plus.version3.0.5/mybatis-plus.versionvelocity.version2.0/velocity.versionswagger.version2.7.0/swagger.versionjwt.version0.7.0/jwt.versionfastjson.version1.2.28/fastjson.versiongson.version2.8.2/gson.versionjson.version20170516/json.versioncloud-alibaba.version0.2.2.RELEASE/cloud-alibaba.version/propertiesdependencyManagementdependencies!--Spring Cloud--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversionHoxton.RELEASE/versiontypepom/typescopeimport/scope/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion${cloud-alibaba.version}/versiontypepom/typescopeimport/scope/dependency!--mybatis-plus 持久层--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion${mybatis-plus.version}/version/dependency!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --dependencygroupIdorg.apache.velocity/groupIdartifactIdvelocity-engine-core/artifactIdversion${velocity.version}/version/dependencydependencygroupIdcom.google.code.gson/groupIdartifactIdgson/artifactIdversion${gson.version}/version/dependency!--swagger--dependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger2/artifactIdversion${swagger.version}/version/dependency!--swagger ui--dependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger-ui/artifactIdversion${swagger.version}/version/dependency!-- JWT --dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion${jwt.version}/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion${fastjson.version}/version/dependencydependencygroupIdorg.json/groupIdartifactIdjson/artifactIdversion${json.version}/version/dependency/dependencies/dependencyManagementbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build /project【2】common模块 common模块pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdacl_parent/artifactIdgroupIdcom.atguigu/groupIdversion0.0.1-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdcommon/artifactIdpackagingpom/packagingmodulesmoduleservice_base/modulemodulespring_security/module/modulesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdscopeprovided /scope/dependency!--mybatis-plus--dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdscopeprovided /scope/dependency!--lombok用来简化实体类需要安装lombok插件--dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdscopeprovided /scope/dependency!--swagger--dependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger2/artifactIdscopeprovided /scope/dependencydependencygroupIdio.springfox/groupIdartifactIdspringfox-swagger-ui/artifactIdscopeprovided /scope/dependency!-- redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!-- spring2.X集成redis所需common-pool2--dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactIdversion2.6.0/version/dependency/dependencies /project【3】common模块-SpringSecurity子模块 1核心配置类 SpringSecurity的核心配置就是继承WebSecurityConfigurerAdapter并注解EnableWebSecurity的配置。这个配置指明了用户名密码的处理方式、请求路径、登录登出控制等和安全相关的配置。 package com.atguigu.security.config;import com.atguigu.security.filter.TokenAuthFilter; import com.atguigu.security.filter.TokenLoginFilter; import com.atguigu.security.security.DefaultPasswordEncoder; import com.atguigu.security.security.TokenLogoutHandler; import com.atguigu.security.security.TokenManager; import com.atguigu.security.security.UnauthEntryPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService;Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {private TokenManager tokenManager;private RedisTemplate redisTemplate;private DefaultPasswordEncoder defaultPasswordEncoder;private UserDetailsService userDetailsService;Autowiredpublic TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,TokenManager tokenManager, RedisTemplate redisTemplate) {this.userDetailsService userDetailsService;this.defaultPasswordEncoder defaultPasswordEncoder;this.tokenManager tokenManager;this.redisTemplate redisTemplate;}/*** 配置设置* param http* throws Exception*///设置退出的地址和tokenredis操作地址Overrideprotected void configure(HttpSecurity http) throws Exception {http.exceptionHandling().authenticationEntryPoint(new UnauthEntryPoint())//没有权限访问.and().csrf().disable().authorizeRequests().anyRequest().authenticated().and().logout().logoutUrl(/admin/acl/index/logout)//退出路径.addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and().addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate)).addFilter(new TokenAuthFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();}//调用userDetailsService和密码处理Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);}//不进行认证的路径可以直接访问Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers(/api/**);} }2实体类 package com.atguigu.security.entity;import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.StringUtils;import java.util.ArrayList; import java.util.Collection; import java.util.List;Data public class SecurityUser implements UserDetails {//当前登录用户private transient User currentUserInfo;//当前权限private ListString permissionValueList;public SecurityUser() {}public SecurityUser(User user) {if (user ! null) {this.currentUserInfo user;}}Overridepublic Collection? extends GrantedAuthority getAuthorities() {CollectionGrantedAuthority authorities new ArrayList();for(String permissionValue : permissionValueList) {if(StringUtils.isEmpty(permissionValue)) continue;SimpleGrantedAuthority authority new SimpleGrantedAuthority(permissionValue);authorities.add(authority);}return authorities;} }package com.atguigu.security.entity;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;import java.io.Serializable;Data ApiModel(description 用户实体类) public class User implements Serializable {private static final long serialVersionUID 1L;ApiModelProperty(value 微信openid)private String username;ApiModelProperty(value 密码)private String password;ApiModelProperty(value 昵称)private String nickName;ApiModelProperty(value 用户头像)private String salt;ApiModelProperty(value 用户签名)private String token;}3过滤器 package com.atguigu.security.filter;import com.atguigu.security.security.TokenManager; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List;public class TokenAuthFilter extends BasicAuthenticationFilter {private TokenManager tokenManager;private RedisTemplate redisTemplate;public TokenAuthFilter(AuthenticationManager authenticationManager,TokenManager tokenManager,RedisTemplate redisTemplate) {super(authenticationManager);this.tokenManager tokenManager;this.redisTemplate redisTemplate;}Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {//获取当前认证成功用户权限信息UsernamePasswordAuthenticationToken authRequest getAuthentication(request);//判断如果有权限信息放到权限上下文中if(authRequest ! null) {SecurityContextHolder.getContext().setAuthentication(authRequest);}chain.doFilter(request,response);}private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {//从header获取tokenString token request.getHeader(token);if(token ! null) {//从token获取用户名String username tokenManager.getUserInfoFromToken(token);//从redis获取对应权限列表ListString permissionValueList (ListString)redisTemplate.opsForValue().get(username);CollectionGrantedAuthority authority new ArrayList();for(String permissionValue : permissionValueList) {SimpleGrantedAuthority auth new SimpleGrantedAuthority(permissionValue);authority.add(auth);}return new UsernamePasswordAuthenticationToken(username,token,authority);}return null;}}package com.atguigu.security.filter;import com.atguigu.security.entity.SecurityUser; import com.atguigu.security.entity.User; import com.atguigu.security.security.TokenManager; import com.atguigu.utils.utils.R; import com.atguigu.utils.utils.ResponseUtil; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList;public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {private TokenManager tokenManager;private RedisTemplate redisTemplate;private AuthenticationManager authenticationManager;public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {this.authenticationManager authenticationManager;this.tokenManager tokenManager;this.redisTemplate redisTemplate;this.setPostOnly(false);this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(/admin/acl/login,POST));}//1 获取表单提交用户名和密码Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException {//获取表单提交数据try {User user new ObjectMapper().readValue(request.getInputStream(), User.class);return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword(),new ArrayList()));} catch (IOException e) {e.printStackTrace();throw new RuntimeException();}}//2 认证成功调用的方法Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)throws IOException, ServletException {//认证成功得到认证成功之后用户信息SecurityUser user (SecurityUser)authResult.getPrincipal();//根据用户名生成tokenString token tokenManager.createToken(user.getCurrentUserInfo().getUsername());//把用户名称和用户权限列表放到redisredisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(),user.getPermissionValueList());//返回tokenResponseUtil.out(response, R.ok().data(token,token));}//3 认证失败调用的方法protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)throws IOException, ServletException {ResponseUtil.out(response, R.error());} }4security package com.atguigu.security.security;import com.atguigu.utils.utils.MD5; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component;Component public class DefaultPasswordEncoder implements PasswordEncoder {public DefaultPasswordEncoder() {this(-1);}public DefaultPasswordEncoder(int strength) {}//进行MD5加密Overridepublic String encode(CharSequence charSequence) {return MD5.encrypt(charSequence.toString());}//进行密码比对Overridepublic boolean matches(CharSequence charSequence, String encodedPassword) {return encodedPassword.equals(MD5.encrypt(charSequence.toString()));} }package com.atguigu.security.security;import com.atguigu.utils.utils.R; import com.atguigu.utils.utils.ResponseUtil; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutHandler;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //退出处理器 public class TokenLogoutHandler implements LogoutHandler {private TokenManager tokenManager;private RedisTemplate redisTemplate;public TokenLogoutHandler(TokenManager tokenManager,RedisTemplate redisTemplate) {this.tokenManager tokenManager;this.redisTemplate redisTemplate;}Overridepublic void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {//1 从header里面获取token//2 token不为空移除token从redis删除tokenString token request.getHeader(token);if(token ! null) {//移除tokenManager.removeToken(token);//从token获取用户名String username tokenManager.getUserInfoFromToken(token);redisTemplate.delete(username);}ResponseUtil.out(response, R.ok());} }package com.atguigu.security.security;import io.jsonwebtoken.CompressionCodecs; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.stereotype.Component;import java.util.Date;Component public class TokenManager {//token有效时长private long tokenEcpiration 24*60*60*1000;//编码秘钥private String tokenSignKey 123456;//1 使用jwt根据用户名生成tokenpublic String createToken(String username) {String token Jwts.builder().setSubject(username).setExpiration(new Date(System.currentTimeMillis()tokenEcpiration)).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}//2 根据token字符串得到用户信息public String getUserInfoFromToken(String token) {String userinfo Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();return userinfo;}//3 删除tokenpublic void removeToken(String token) { } }package com.atguigu.security.security;import com.atguigu.utils.utils.R; import com.atguigu.utils.utils.ResponseUtil; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;public class UnauthEntryPoint implements AuthenticationEntryPoint {Overridepublic void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {ResponseUtil.out(httpServletResponse, R.error());} }【4】common模块-service_base 1RedisConfig package com.atguigu.utils;import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;EnableCaching //开启缓存 Configuration //配置类 public class RedisConfig extends CachingConfigurerSupport {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) {RedisTemplateString, Object template new RedisTemplate();RedisSerializerString redisSerializer new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializerString redisSerializer new StringRedisSerializer();Jackson2JsonRedisSerializer jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题ObjectMapper om new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化解决乱码的问题,过期时间600秒RedisCacheConfiguration config RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;} }2SwaggerConfig package com.atguigu.utils;import com.google.common.base.Predicates; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;Configuration//配置类 EnableSwagger2 //swagger注解 public class SwaggerConfig {Beanpublic Docket webApiConfig(){return new Docket(DocumentationType.SWAGGER_2).groupName(webApi).apiInfo(webApiInfo()).select()//.paths(Predicates.not(PathSelectors.regex(/admin/.*))).paths(Predicates.not(PathSelectors.regex(/error.*))).build();}private ApiInfo webApiInfo(){return new ApiInfoBuilder().title(网站-课程中心API文档).description(本文档描述了课程中心微服务接口定义).version(1.0).contact(new Contact(java, http://atguigu.com, 1123qq.com)).build();} }3工具类 package com.atguigu.utils.utils;import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;public final class MD5 {public static String encrypt(String strSrc) {try {char hexChars[] { 0, 1, 2, 3, 4, 5, 6, 7, 8,9, a, b, c, d, e, f };byte[] bytes strSrc.getBytes();MessageDigest md MessageDigest.getInstance(MD5);md.update(bytes);bytes md.digest();int j bytes.length;char[] chars new char[j * 2];int k 0;for (int i 0; i bytes.length; i) {byte b bytes[i];chars[k] hexChars[b 4 0xf];chars[k] hexChars[b 0xf];}return new String(chars);} catch (NoSuchAlgorithmException e) {e.printStackTrace();throw new RuntimeException(MD5加密出错 e);}}public static void main(String[] args) {System.out.println(MD5.encrypt(111111));} }package com.atguigu.utils.utils;import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType;import javax.servlet.http.HttpServletResponse; import java.io.IOException;public class ResponseUtil {public static void out(HttpServletResponse response, R r) {ObjectMapper mapper new ObjectMapper();response.setStatus(HttpStatus.OK.value());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);try {mapper.writeValue(response.getWriter(), r);} catch (IOException e) {e.printStackTrace();}} }package com.atguigu.utils.utils;import lombok.Data; import java.util.HashMap; import java.util.Map;//统一返回结果的类 Data public class R {private Boolean success;private Integer code;private String message;private MapString, Object data new HashMapString, Object();//把构造方法私有private R() {}//成功静态方法public static R ok() {R r new R();r.setSuccess(true);r.setCode(20000);r.setMessage(成功);return r;}//失败静态方法public static R error() {R r new R();r.setSuccess(false);r.setCode(20001);r.setMessage(失败);return r;}public R success(Boolean success){this.setSuccess(success);return this;}public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(MapString, Object map){this.setData(map);return this;} }【5】gateway模块 1pom.xml ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdinfrastructure/artifactIdgroupIdcom.atguigu/groupIdversion0.0.1-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdapi_gateway/artifactIddependenciesdependencygroupIdcom.atguigu/groupIdartifactIdservice_base/artifactIdversion0.0.1-SNAPSHOT/version/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId/dependency!--gson--dependencygroupIdcom.google.code.gson/groupIdartifactIdgson/artifactId/dependency!--服务调用--dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency/dependencies /project2application.properties # 端口号 server.port8222 # 服务名 spring.application.nameservice-gateway # nacos服务地址 spring.cloud.nacos.discovery.server-addr127.0.0.1:8848 # 使用服务发现路由 spring.cloud.gateway.discovery.locator.enabledtrue# 配置路由规则 spring.cloud.gateway.routes[0].idservice-acl # 设置路由uri lb://注册服务名称 spring.cloud.gateway.routes[0].urilb://service-acl # 具体路径规则 spring.cloud.gateway.routes[0].predicates Path/*/acl/**3解决跨域 package com.atguigu.gateway.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser;Configuration public class CorsConfig {//解决跨域Beanpublic CorsWebFilter corsWebFilter() {CorsConfiguration config new CorsConfiguration();config.addAllowedMethod(*);config.addAllowedOrigin(*);config.addAllowedHeader(*);UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(new PathPatternParser());source.registerCorsConfiguration(/**,config);return new CorsWebFilter(source);} }【八】ruoyi平台整合SpringSecurity案例【待完成】
http://www.w-s-a.com/news/368986/

相关文章:

  • wordpress 微网站模板怎么用wordpress 文档下载
  • ppt网站建设的目的合肥做网站找哪家好
  • wordpress站点路径redis缓存wordpress
  • 专门设计网站的公司叫什么百度 门户网站
  • 网站建设丶金手指专业旅游网站系统哪个好
  • 苏州工业园区两学一做网站成都企业排名
  • 医药网站开发wordpress境外支付
  • 营销自己的网站网站如何做标题优化
  • 玖云建站系统wordpress nodejs版本
  • 网站开发费用计入什么二级科目重庆企业网站推广
  • wordpress 菜单怎么使用方法宜春网站推广优化
  • dede 网站图标怎么自学建筑设计
  • 河北斯皮尔网站建设做微信小程序和做网站
  • 沈阳市住房和城乡建设局网站创意上海专业网站建设
  • 免费学编程国内网站it需要什么学历
  • 相城做网站的公司网站建设范本
  • 怎么样查中企动力做的网站阿里邮箱企业版手机版
  • 电子商务网站建设与管理试卷6江门网站建设联系电话
  • 公司的网站建设做什么费用四川圣泽建设集团有限公司网站
  • 为什么网站很少做全屏福利WordPress网站自动采集源码
  • 网站备案法律diy
  • 淘宝客如何新建网站物业管理系统app
  • 品牌网站建设策重大军事新闻视频
  • 廊坊建设网站的公司wordpress清理无用缩略图
  • 桓台网站建设公司首钢建设二建设公司网站
  • 网站建设添加背景命令前端如何优化网站性能
  • 设置网站域名中山画册设计公司
  • 三更app下载网站东莞网站制作公
  • 做图书馆网站模板网站建设文化策划方案
  • 惠州城乡住房建设厅网站服装设计自学零基础