xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.0.1.RELEASE
sec_test
sec
0.0.1-SNAPSHOT
war
UTF-8
2.0.1.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
runtime
com.alibaba
druid
1.0.19
com.google.code.gson
gson
2.8.5
org.projectlombok
lombok
1.16.20
provided
${project.name}
maven-compiler-plugin
1.8
org.springframework.boot
spring-boot-maven-plugin
server:
port: 8090
application:
name: sec
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
content-type: text/html
cache: false #开发时关闭缓存,不然没法看到实时页面!
prefix: classpath:/public/ #配置页面文件路径
suffix: .html #配置页面默认后缀
datasource:
url: jdbc:mysql://127.0.0.1:3306/sec?useUnicode=true&characterEncoding=UTF-8
username: root
password: ******
package com.veiking;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 项目启动入口
* @author Veiking
*/
@SpringBootApplication
public class SecApplication {
public static void main(String[] args) {
SpringApplication.run(SecApplication.class, args);
}
}
-- ----------------------------
-- Table structure for `s_user`
-- ----------------------------
DROP TABLE IF EXISTS `s_user`;
CREATE TABLE `s_user` (
`id` int(11) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`password` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `s_user_role`
-- ----------------------------
DROP TABLE IF EXISTS `s_user_role`;
CREATE TABLE `s_user_role` (
`fk_user_id` int(11) DEFAULT NULL,
`fk_role_id` int(11) DEFAULT NULL,
KEY `union_key` (`fk_user_id`,`fk_role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `s_role`
-- ----------------------------
DROP TABLE IF EXISTS `s_role`;
CREATE TABLE `s_role` (
`id` int(11) NOT NULL,
`role` varchar(32) DEFAULT NULL,
`describe` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `s_role_permission`
-- ----------------------------
DROP TABLE IF EXISTS `s_role_permission`;
CREATE TABLE `s_role_permission` (
`fk_role_id` int(11) DEFAULT NULL,
`fk_permission_id` int(11) DEFAULT NULL,
KEY `union_key` (`fk_role_id`,`fk_permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `s_permission`
-- ----------------------------
DROP TABLE IF EXISTS `s_permission`;
CREATE TABLE `s_permission` (
`id` int(11) NOT NULL,
`permission` varchar(32) DEFAULT NULL,
`url` varchar(32) DEFAULT NULL,
`describe` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of s_user
-- ----------------------------
INSERT INTO `s_user` VALUES ('1', 'admin', 'admin');
INSERT INTO `s_user` VALUES ('2', 'veiking', 'veiking');
INSERT INTO `s_user` VALUES ('3', 'xiaoming', 'xiaoming');
-- ----------------------------
-- Records of s_user_role
-- ----------------------------
INSERT INTO `s_user_role` VALUES ('1', '1');
INSERT INTO `s_user_role` VALUES ('2', '2');
-- ----------------------------
-- Records of s_role
-- ----------------------------
INSERT INTO `s_role` VALUES ('1', 'R_ADMIN', '大总管,所有权限');
INSERT INTO `s_role` VALUES ('2', 'R_HELLO', '说hello相关的权限');
-- ----------------------------
-- Records of s_role_permission
-- ----------------------------
INSERT INTO `s_role_permission` VALUES ('1', '1');
INSERT INTO `s_role_permission` VALUES ('1', '2');
INSERT INTO `s_role_permission` VALUES ('1', '3');
INSERT INTO `s_role_permission` VALUES ('2', '1');
INSERT INTO `s_role_permission` VALUES ('2', '3');
-- ----------------------------
-- Records of s_permission
-- ----------------------------
INSERT INTO `s_permission` VALUES ('1', 'P_INDEX', '/index', 'index页面资源');
INSERT INTO `s_permission` VALUES ('2', 'P_ADMIN', '/admin', 'admin页面资源');
INSERT INTO `s_permission` VALUES ('3', 'P_HELLO', '/hello', 'hello页面资源');
登录
INDEX
| ADMIN
| HELLO
已成功注销
有错误,请重试
使用用户名密码登录
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
主页
INDEX
你好:
INDEX
| ADMIN
| HELLO
package com.veiking.sec.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户名密码信息
* @author Veiking
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SUser {
private int id;
private String name;
private String password;
public SUser(SUser sUser) {
this.id = sUser.getId();
this.name = sUser.getName();
this.password = sUser.getPassword();
}
}
package com.veiking.sec.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 角色信息
* @author Veiking
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SRole {
private int id;
private String role;
private String describe;
}
package com.veiking.sec.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 访问资源信息
* @author Veiking
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SPermission {
private int id;
private String permission;
private String url;
private String describe;
}
package com.veiking.sec.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.veiking.sec.bean.SUser;
/**
* 用户信息查询
* @author Veiking
*/
@Mapper
public interface SUserDao {
/**
* 根据用户名获取用户
*
* @param name
* @return
*/
@Select(value = " SELECT su.* FROM s_user su WHERE su.name = #{name} ")
public SUser findSUserByName(String name);
}
package com.veiking.sec.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.veiking.sec.bean.SRole;
/**
* 角色信息查询
* @author Veiking
*/
@Mapper
public interface SRoleDao {
/**
* 根据用户ID获取角色列表
* @param sUserId
* @return
*/
@Select(value=" SELECT sr.* FROM s_role sr " +
" LEFT JOIN s_user_role sur ON sr.id = sur.fk_role_id " +
" LEFT JOIN s_user su ON sur.fk_user_id = su.id " +
" WHERE su.id = #{sUserId} ")
public List findSRoleListBySUserId(int sUserId);
/**
* 根据资源路径获取角色列表
* @param sPermissionUrl
* @return
*/
@Select(value=" SELECT sr.* FROM s_role sr " +
" LEFT JOIN s_role_permission srp ON sr.id = srp.fk_role_id " +
" LEFT JOIN s_permission sp ON srp.fk_permission_id = sp.id " +
" WHERE sp.url = #{sPermissionUrl} ")
public List findSRoleListBySPermissionUrl(String sPermissionUrl);
}
package com.veiking.sec.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import com.veiking.sec.bean.SPermission;
/**
* 资源权限信息查询
* @author Veiking
*/
@Mapper
public interface SPermissionDao {
/**
* 根据用户ID获取资源权限列表
* @param sUserId
* @return
*/
@Select(value=" SELECT * FROM s_permission sp " +
" LEFT JOIN s_role_permission srp ON sp.id = srp.fk_permission_id " +
" LEFT JOIN s_role sr ON srp.fk_role_id = sr.id " +
" LEFT JOIN s_user_role sur ON sr.id = sur.fk_role_id " +
" LEFT JOIN s_user su ON sur.fk_user_id = su.id " +
" WHERE su.id = #{sUserId} ")
public List findSPermissionListBySUserId(int sUserId);
/**
* 根据资源路径获取资源权限列表
* @param sPermissionUrl
* @return
*/
@Select(value=" SELECT * FROM s_permission sp WHERE sp.url = #{sUserId} ")
public List findSPermissionListBySPermissionUrl(String sPermissionUrl);
}
package com.veiking.sec.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.veiking.sec.bean.SPermission;
import com.veiking.sec.bean.SRole;
import com.veiking.sec.bean.SUser;
import com.veiking.sec.dao.SPermissionDao;
import com.veiking.sec.dao.SRoleDao;
import com.veiking.sec.dao.SUserDao;
/**
* Security 数据服务
*
* @author Veiking
*/
@Service
public class SecurityDataService {
@Autowired
private SUserDao sUserDao;
@Autowired
private SRoleDao sRoleDao;
@Autowired
private SPermissionDao sPermissionDao;
public SUser findSUserByName(String name) {
return sUserDao.findSUserByName(name);
}
public List findSRoleListBySUserId(int sUserId) {
return sRoleDao.findSRoleListBySUserId(sUserId);
}
public List findSRoleListBySPermissionUrl(String sPermissionUrl) {
return sRoleDao.findSRoleListBySPermissionUrl(sPermissionUrl);
}
public List findSPermissionListBySUserId(int sUserId) {
return sPermissionDao.findSPermissionListBySUserId(sUserId);
}
public List findSPermissionListBySPermissionUrl(String sPermissionUrl) {
return sPermissionDao.findSPermissionListBySPermissionUrl(sPermissionUrl);
}
}
package com.veiking.sec.authentication;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import com.google.gson.Gson;
import com.veiking.sec.bean.SPermission;
import com.veiking.sec.bean.SRole;
import com.veiking.sec.bean.SUser;
/**
* 用户信息的封装,包含用户名称密码及用户状态、权限等信息
* @author Veiking
*/
public class VUserDetails extends SUser implements UserDetails{
private static final long serialVersionUID = 1L;
Gson gson = new Gson();
Logger logger = LoggerFactory.getLogger(this.getClass());
//用户角色列表
private List sRoleList = null;
//用户资源权限列表
private List sPermissionList = null;
/**
* 注意后边的这两个参数:sRoleList、sPermissionList
* @param sUser
* @param sRoleList
* @param sPermissionList
*/
public VUserDetails(SUser sUser, List sRoleList, List sPermissionList) {
super(sUser);
this.sRoleList = sRoleList;
this.sPermissionList = sPermissionList;
}
/**
* 获取用户权限列表方法
* 可以理解成,返回了一个List,之后所谓的权限控制、鉴权,其实就是跟这个list里的String进行对比
* 这里处理了角色和资源权限两个列表,可以这么理解,
* 角色是权限的抽象集合,是为了更方便的控制和分配权限,而真正颗粒化细节方面,还是需要资源权限自己来做
*/
@Override
public Collection extends GrantedAuthority> getAuthorities() {
StringBuilder authoritiesBuilder = new StringBuilder("");
List tempRoleList = this.getsRoleList();
if (null != tempRoleList) {
for (SRole role : tempRoleList) {
authoritiesBuilder.append(",").append(role.getRole());
}
}
List tempPermissionList = this.getsPermissionList();
if (null != tempPermissionList) {
for (SPermission permission : tempPermissionList) {
authoritiesBuilder.append(",").append(permission.getPermission());
}
}
String authoritiesStr = "";
if(authoritiesBuilder.length()>0) {
authoritiesStr = authoritiesBuilder.deleteCharAt(0).toString();
}
logger.info("VUserDetails getAuthorities [authoritiesStr={} ", authoritiesStr);
return AuthorityUtils.commaSeparatedStringToAuthorityList(authoritiesStr);
}
@Override
public String getPassword() {
return super.getPassword();
}
@Override
public String getUsername() {
return super.getName();
}
/**
* 判断账号是否已经过期,默认没有过期
*/
@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}
/**
* 判断账号是否被锁定,默认没有锁定
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 判断信用凭证是否过期,默认没有过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 判断账号是否可用,默认可用
*/
@Override
public boolean isEnabled() {
return true;
}
public List getsRoleList() {
return sRoleList;
}
public void setsRoleList(List sRoleList) {
this.sRoleList = sRoleList;
}
public List getsPermissionList() {
return sPermissionList;
}
public void setsPermissionList(List sPermissionList) {
this.sPermissionList = sPermissionList;
}
}
package com.veiking.sec.authentication;
import java.util.List;
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.stereotype.Service;
import com.veiking.sec.bean.SPermission;
import com.veiking.sec.bean.SRole;
import com.veiking.sec.bean.SUser;
import com.veiking.sec.service.SecurityDataService;
/**
* 提供用户信息封装服务
* @author Veiking
*/
@Service
public class VUserDetailsService implements UserDetailsService {
@Autowired
SecurityDataService securityDataService;
/**
* 根据用户输入的用户名返回数据源中用户信息的封装,返回一个UserDetails
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SUser sUser = securityDataService.findSUserByName(username);
//用户角色列表
List sRoleList = securityDataService.findSRoleListBySUserId(sUser.getId());
//用户资源权限列表
List sPermissionList = securityDataService.findSPermissionListBySUserId(sUser.getId());
return new VUserDetails(sUser, sRoleList, sPermissionList);
}
}
package com.veiking.sec.authentication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
/**
* 认证提供者,校验用户,登录名密码,以及向系统提供一个用户信息的综合封装
* @author Veiking
*/
@Component
public class VAuthenticationProvider implements AuthenticationProvider {
Gson gson = new Gson();
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
VUserDetailsService vUserDetailsService;
/**
* 首先,在用户登录的时候,系统将用户输入的的用户名和密码封装成一个Authentication对象
* 然后,根据用户名去数据源中查找用户的数据,这个数据是封装成的VUserDetails对象
* 接着,将两个对象进行信息比对,如果密码正确,通过校验认证
* 最后,将用户信息(含身份信息、细节信息、密码、权限等)封装成一个对象,此处参考UsernamePasswordAuthenticationToken
* 最最后,会将这个对象交给系统SecurityContextHolder中(功能类似Session),以便后期随时取用
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
logger.info("VAuthenticationProvider authenticate login user [username={}, password={}]", username, password);
VUserDetails vUserDetails = (VUserDetails)vUserDetailsService.loadUserByUsername(username);
logger.info("VAuthenticationProvider authenticate vUserDetails [vUserDetails={}]", gson.toJson(vUserDetails));
if(vUserDetails == null){
throw new BadCredentialsException("用户没有找到");
}
if (!password.equals(vUserDetails.getPassword())) {
logger.info("VAuthenticationProvider authenticate BadCredentialsException [inputPassword={}, DBPassword={}]", password, vUserDetails.getPassword());
throw new BadCredentialsException("密码错误");
}
//认证校验通过后,封装UsernamePasswordAuthenticationToken返回
return new UsernamePasswordAuthenticationToken(vUserDetails, password, vUserDetails.getAuthorities());
}
@Override
public boolean supports(Class> authentication) {
return true;
}
}
package com.veiking.sec;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 访问路径配置类
* 可以理解成做简单访问过滤的,转发到相应的视图页面
* @author Veiking
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
}
}
package com.veiking.sec.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 请求页面分发,注意和WebMvcConfig的对比,功能类似
* @author Veiking
*/
@Controller
public class PageController {
@RequestMapping("/admin")
public String admin(Model model, String tt) {
return "admin";
}
@RequestMapping("/hello")
public String hello(Model model, String tt) {
return "hello";
}
}
package com.veiking.sec;
import org.springframework.context.annotation.Configuration;
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;
/**
* Security 主配置文件
* @author Veiking
*/
@Configuration
@EnableWebSecurity //开启Spring Security的功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定义不需要过滤的静态资源(等价于HttpSecurity的permitAll)
*/
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/css/**");
}
/**
* 安全策略配置
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
// 对于网站部分资源需要指定鉴权
//.antMatchers("/admin/**").hasRole("ADMIN")
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated().and()
// 定义当需要用户登录时候,转到的登录页面
.formLogin().loginPage("/login").defaultSuccessUrl("/index").permitAll().and()
// 定义登出操作
.logout().logoutSuccessUrl("/login?logout").permitAll().and()
.csrf().disable()
;
// 禁用缓存
httpSecurity.headers().cacheControl();
}
}
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
HELLO
HELLO
你好:
INDEX
| ADMIN
| HELLO
package com.veiking.sec.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 请求页面分发,注意和WebMvcConfig的对比,功能类似
* @author Veiking
*/
@Controller
public class PageController {
@RequestMapping("/admin")
@PreAuthorize("hasAuthority('R_ADMIN')")
public String admin(Model model, String tt) {
return "admin";
}
@RequestMapping("/hello")
public String hello(Model model, String tt) {
return "hello";
}
}
package com.veiking.sec;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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;
/**
* Security 主配置文件
* @author Veiking
*/
@Configuration
@EnableWebSecurity //开启Spring Security的功能
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启注解控制权限
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 定义不需要过滤的静态资源(等价于HttpSecurity的permitAll)
*/
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity.ignoring().antMatchers("/css/**");
}
/**
* 安全策略配置
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
// 对于网站部分资源需要指定鉴权
//.antMatchers("/admin/**").hasRole("ADMIN")
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated().and()
// 定义当需要用户登录时候,转到的登录页面
.formLogin().loginPage("/login").defaultSuccessUrl("/index").permitAll().and()
// 定义登出操作
.logout().logoutSuccessUrl("/login?logout").permitAll().and()
.csrf().disable()
;
// 禁用缓存
httpSecurity.headers().cacheControl();
}
/**
* 开启注解控制权限
* 见Controller 中 @PreAuthorize("hasAuthority('XXX')")
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
package com.veiking.sec.authorization;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 com.google.gson.Gson;
import com.veiking.sec.bean.SPermission;
import com.veiking.sec.bean.SRole;
import com.veiking.sec.service.SecurityDataService;
/**
* 权限资源管理器
* 根据用户请求的地址,获取访问该地址需要的所需权限
* @author Veiking
*/
@Component
public class VFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
Gson gson = new Gson();
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
SecurityDataService securityDataService;
@Override
public Collection getAttributes(Object object) throws IllegalArgumentException {
//获取请求起源路径
String requestUrl = ((FilterInvocation) object).getRequestUrl();
logger.info("VFilterInvocationSecurityMetadataSource getAttributes [requestUrl={}]", requestUrl);
//登录页面就不需要权限
if ("/login".equals(requestUrl)) {
return null;
}
//用来存储访问路径需要的角色或权限信息
List tempPermissionList = new ArrayList();
//获取角色列表
List sRoleList = securityDataService.findSRoleListBySPermissionUrl(requestUrl);
logger.info("VFilterInvocationSecurityMetadataSource getAttributes [sRoleList={}]", gson.toJson(sRoleList));
for(SRole sRole : sRoleList) {
tempPermissionList.add(sRole.getRole());
}
//径获取资源权限列表
List sPermissionList = securityDataService.findSPermissionListBySPermissionUrl(requestUrl);
logger.info("VFilterInvocationSecurityMetadataSource getAttributes [sPermissionList={}]", gson.toJson(sPermissionList));
for(SPermission sPermission : sPermissionList) {
tempPermissionList.add(sPermission.getPermission());
}
//如果没有权限控制的url,可以设置都可以访问,也可以设置默认不许访问
if(tempPermissionList.isEmpty()) {
return null;//都可以访问
//tempPermissionList.add("DEFAULT_FORBIDDEN");//默认禁止
}
String[] permissionArray = tempPermissionList.toArray(new String[0]);
logger.info("VFilterInvocationSecurityMetadataSource getAttributes [permissionArray={}]", gson.toJson(permissionArray));
return SecurityConfig.createList(permissionArray);
}
@Override
public Collection getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean supports(Class> clazz) {
return true;
}
}
package com.veiking.sec.authorization;
import java.util.Collection;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
/**
* 权限管理判断器|校验用户是否有权限访问请求资源
* @author Veiking
*/
@Component
public class VAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
//当前用户所具有的权限
Collection extends GrantedAuthority> userAuthorityList = authentication.getAuthorities();
//访问资源所需的权限信息
Collection needAuthoritieList = configAttributes;
//依次循环对比,发现有匹配的即返回
for(ConfigAttribute needAuthoritie: needAuthoritieList) {
String needAuthoritieStr = needAuthoritie.getAttribute();
for (GrantedAuthority userAuthority : userAuthorityList) {
String userAuthorityStr = userAuthority.getAuthority();
if (needAuthoritieStr.equals(userAuthorityStr)) {
return;
}
}
}
//执行到这里说明没有匹配到应有权限
throw new AccessDeniedException("权限不足!");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class> clazz) {
return true;
}
}
package com.veiking.sec.authorization;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
/**
* 访问鉴权过滤器
* 该过滤器的作用就是,用户请求时,提供权限资源管理器和权限判断器工作的场所,实现鉴权操作
* @author Veiking
*/
@Component
@ServletComponentScan
@WebFilter(filterName="vFilterSecurityInterceptor",urlPatterns="/*")
public class VFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private VFilterInvocationSecurityMetadataSource vFilterInvocationSecurityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(VAccessDecisionManager vAccessDecisionManager) {
super.setAccessDecisionManager(vAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FilterInvocation filterInvocation = new FilterInvocation(request, response, chain);
invoke(filterInvocation);
}
public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
// filterInvocation里面有一个被拦截的url
// 里面调用VFilterInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取filterInvocation对应的所有权限
// 再调用VAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken interceptorStatusToken = super.beforeInvocation(filterInvocation);
try {
// 执行下一个拦截器
filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
} finally {
super.afterInvocation(interceptorStatusToken, null);
}
}
@Override
public void destroy() {
}
@Override
public Class> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.vFilterInvocationSecurityMetadataSource;
}
}