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;
? ? }
?}
??
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<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;
? ? }
?}
?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;
? ? }
?}
?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/**");
? ? ? }
?}
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>
?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);
? ? }
?}
?
我们发同一个 页面, 不同的 角色 ,权限不同, 因此会 看到不同的 页面显示 这就是 基于路径的权限控制