Spring Security-动态权限控制(2)

6创建service层

MenuService.java

 
package com.service;
 ​
 import com.entity.Menu;
 import com.mapper.MenuMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 ​
 import java.util.List;
 ​
 @Service
 public class MenuService {
 ​
     @Autowired
     MenuMapper menuMapper;
 ​
    public  List<Menu> getAllMenus(){
         return menuMapper.getAllMenus();
     }
 ​
 ​
 }

MyUserDetailsService.java

 package com.service;
 ​
 import com.entity.User;
 import com.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.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
 ​
 ​
 @Service("userDetailsService")
 public class MyUserDetailsService implements UserDetailsService {
 ​
     @Autowired
     private UserMapper userMapper;
 ​
 ​
 ​
     @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 ​
         //根据用户名 查询 --调用mapper进行查询----- User 是咱 自己的entity,
         //因为他class User implements      UserDetails  ,这里不要引错
        User info = userMapper.loadUserByUsername(username);  
         //判断
         if(info==null){   //没有用户 验证失败
             throw new UsernameNotFoundException("用户名不存在");
         }
         //设置密码--注意加密
         info.setPassword(new BCryptPasswordEncoder().encode(info.getPassword()));
         //根据当前用户id 查询 当前 用户的角色
         info.setRoles(userMapper.getUserRolesById(info.getId()));
         //返回User
         return info;
     }
 }
 ​

7创建 controller层java

TestController.java

新增,导出 修改,删除 查询的 url 已经再数据库 menu中进行配置

 
package com.controller;
 ​
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 ​
 ​
 @Controller
 @RequestMapping("/test")
 public class TestController {
 ​
     @GetMapping("/add")  //新增
     @ResponseBody
     public String add(){ return "hello add"; }
     @GetMapping("/export") //导出
     @ResponseBody
     public String export(){
         return "hello export";
     }
 ​
     @GetMapping("/update") //修改
     @ResponseBody
     public String update(){
         return "hello update";
     }
 ​
     @GetMapping("/delete") //删除
     @ResponseBody
     public String delete(){
         return "hello delete";
     }
 ​
     @GetMapping("/query")  //查询
     @ResponseBody
     public String query(){
         return "hello query";
     }
     
     //自定义登录页面的url
     @GetMapping("/toLogin")
     public String login(){
         return "login";
     }
     //认证成功过后 进入主页
     @GetMapping("/success")
     public String success(){
         return "main";
     }
 }

8创建MyAccessDecisionManager 访问决策管理器

AccessDecisionManager是一个接口,声明了三个方法,核心方法是decide用以授权,另外两个supports方法主要起辅助作用,大都执行检查操作的。

 
package com.config;
 ​
 import org.springframework.security.access.AccessDecisionManager;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.InsufficientAuthenticationException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.stereotype.Component;
 ​
 import java.util.Collection;
 ​
 @Component
 public class MyAccessDecisionManager implements AccessDecisionManager {
     @Override
     public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
 ​
         for(ConfigAttribute attribute:configAttributes){
            
             if("ROLE_login".equals(attribute.getAttribute())){
                 //路径不在数据库配置范围
                 if(authentication instanceof AnonymousAuthenticationToken){ // 用户未登录
 ​
                  
                     throw new AccessDeniedException("非法请求尚未登录 ,请先登录");
                 }else{
                     return ; // 用户已经登录, 无需判断, 方法到此结束
                 }
 ​
             }
             // 获取当前登录用户的角色
             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;
     }
 }

9 创建过滤器

 package com.filter;
 ​
 import com.entity.Menu;
 import com.entity.Role;
 import com.service.MenuService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.SecurityConfig;
 import org.springframework.security.web.FilterInvocation;
 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
 import org.springframework.stereotype.Component;
 import org.springframework.util.AntPathMatcher;
 ​
 import java.util.Collection;
 import java.util.List;
 ​
 @Component
 public class MyFilter implements FilterInvocationSecurityMetadataSource {
     @Autowired
     public MenuService menuService;
 ​
     //路径匹配符  直接用于匹配路径
     AntPathMatcher pathMatcher=new AntPathMatcher();
 ​
 ​
     @Override
     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String url = ((FilterInvocation)object).getRequestUrl();//获取请求地址
         List<Menu> allMenus = menuService.getAllMenus();
 ​
         for(Menu menu:allMenus){
             if(pathMatcher.match(menu.getPattern(),url)){
                 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");
     }
 ​
     @Override
     public Collection<ConfigAttribute> getAllConfigAttributes() {
         return null;
     }
 ​
     @Override
     public boolean supports(Class<?> clazz) {
         return true;
     }
 }

10创建配置类

 package com.config;
 ​
 ​
 import com.filter.MyFilter;
 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.ObjectPostProcessor;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.firewall.HttpFirewall;
 import org.springframework.security.web.firewall.StrictHttpFirewall;
 ​
 ​
 @Configuration    //配置类
 public class SecurityConfig  {
 ​
     @Autowired
     MyFilter myFilter;
 ​
     @Autowired
     MyAccessDecisionManager myAccessDecisionManager;
 ​
     
 ​
     @Bean
     PasswordEncoder passwordEncoder(){
         return new BCryptPasswordEncoder();
     }
 ​
 ​
     @Bean
     public HttpFirewall allowUrlEncodedslashHttpFirewall() {
         StrictHttpFirewall firewall = new StrictHttpFirewall();//此处可添加别的规则,目前只设置允许双
         firewall.setAllowUrlEncodedDoubleSlash(true);
         return firewall;
     }
 ​
 ​
     @Bean
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
 ​
 ​
         //配置没有权限访问跳转的页面
         http.exceptionHandling().accessDeniedPage("/403.html");
         http.authorizeRequests()  //开始请求权限配置。
                 .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                     @Override
                     public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                         object.setAccessDecisionManager(myAccessDecisionManager);
                         object.setSecurityMetadataSource(myFilter);
                         return object;
                     }
                 })
                 //.anyRequest().authenticated()
                 .and()
                 .formLogin()
                             .loginPage("/test/toLogin")   //登录页面显示
                             .loginProcessingUrl("/user/login")  //html form action
                             .defaultSuccessUrl("/test/success",true)
                 .permitAll()
 ​
 ​
                 .and()
                 .csrf().disable();
         return http.build();
     }
 ​
 ​
     @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
         return (web) -> web.ignoring().antMatchers("/images/**", "/js/**", "/css/**");
      }
 }

11创建页面

login.html

 
<!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org">
 <head>
     <meta charset="UTF-8">
     <title>登录页面</title>
     <link rel="stylesheet" th:href="@{/css/login.css}" />
 </head>
 <body>
 ​
    <form action="/user/login" method="post">
   <input type="text" name="username" placeholder="输入用户名"/><br/>
   <input type="password" name="password" placeholder="输入密码"/><br/>
   <input type="submit" value="登录"/>
 </form>
 </body>
 </html>

main.html

 
<!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
     <link rel="stylesheet" th:href="@{/css/main.css}" />
 </head>
 <body>
 <h1> 这是 主页</h1>
 <div sec:authorize="isAuthenticated()">
    <span sec:authentication="name" style="color: #007bff"></span>您好
 ​
     <div sec:authorize="hasRole('admin')">
         <h1>管理员</h1>
         <a th:href="@{/test/add}">增加1</a>
         <a th:href="@{/test/update}">修改1</a>
         <a th:href="@{/test/delete}">删除1</a>
         <a th:href="@{/test/export}">导出1</a>
 ​
         <a th:href="@{/test/query}">查询1</a>
     </div>
 ​
     <div sec:authorize="!hasRole('admin')">
         <h1>普通用户</h1>
 ​
         <a th:href="@{/test/export}">导出</a>
 ​
         <a th:href="@{/test/query}">查询</a>
     </div>
 ​
 ​
 </div>
 ​
 ​
 </body>
 </html>

12 启动类

 package com;
 ​
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 ​
 ​
 @SpringBootApplication
 @MapperScan(basePackages = "com.mapper")
 public class SS1222App {
     public static void main(String[] args) {
         SpringApplication.run(SS1222App.class,args);
     }
 }

13 启动测试

 

我们发同一个 页面, 不同的 角色 ,权限不同, 因此会 看到不同的 页面显示 这就是 基于路径的权限控制

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Security是一个强大的安全框架,可以实现RBAC(Role-Based Access Control)权限管理系统。它是Spring家族的成员之一,与其他成员(如Spring Boot、Spring Cloud)进行整合时具有无可比拟的优势。 在企业应用中,权限管理是非常重要的一部分内容。Spring Security提供了丰富的功能,包括认证(Authentication)和授权(Authorization)。通过Spring Security,开发人员可以轻松地实现用户的身份认证和访问控制Spring Security支持多种认证方式,包括基于表单的认证、基于HTTP Basic的认证、基于LDAP的认证等。它还提供了强大的授权机制,可以通过角色(Role)和权限(Permission)对用户进行控制。开发人员可以根据<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [spring-security-rbac:Spring Security实现RBAC权限管理](https://download.csdn.net/download/weixin_42164931/18754156)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringSecurity权限管理](https://blog.csdn.net/z318913/article/details/122832600)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

射手座的程序媛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值