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
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;
}
}
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";
}
}
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 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;
}
}
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 getAttributes(Object object) throws IllegalArgumentException {
String url = ((FilterInvocation)object).getRequestUrl();//获取请求地址
List
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() {
@Override
public 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/**");
}
}
login.html
登录页面
main.html
Title
这是 主页
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);
}
}
我们发同一个 页面, 不同的 角色 ,权限不同, 因此会 看到不同的 页面显示 这就是 基于路径的权限控制