springboot+spring security+mybaits实现登录权限管理
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yangl1337905751/article/details/79741695
序:
本文采用springboot+spring security+mybatis来解决登录权限管理的问题。由于是新人,所以在操作方面讲得比较详细。
话不多说,直接来开始我们的项目。
1 创建springboot+spring security+mybatis 项目
这里我们采用了intellIJidea编辑器。
1.1 创建springboot项目(jdk采用1.8版本)
1.2 对项目进行配置
1.3 勾选我们需要的架包依赖
1.4 对项目进行最终的命名及项目位置确定
2 对application.properties以及pom.xml文件进行配置
2.1 application.properties(我们可以随时修改其中数据库的地址及其他相关数据)
-
jdbc.db.driverClassName=com.mysql.cj.jdbc.Driver
-
jdbc.db.url=jdbc:mysql://localhost:3306/mydemo?serverTimezone=UTC&characterEncoding=utf-8&useSSL=false
-
jdbc.db.username=root
-
jdbc.db.password=admin
-
jdbc.db.maxActive=500
-
logging.level.org.springframework.security= INFO
-
spring.thymeleaf.cache=false
2.2 pom.xml(若你按照我的方式创建项目有可能运行不起来,可能是架包版本的缘故,可复制我的)
-
<?xml version="1.0" encoding="UTF-8"?>
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<modelVersion>4.0.0</modelVersion>
-
<groupId>com.example</groupId>
-
<artifactId>security-mybatis</artifactId>
-
<version>1.0-SNAPSHOT</version>
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.0.RELEASE</version>
-
</parent>
-
<properties>
-
<start-class>com.us.Application</start-class>
-
<maven.compiler.target>1.8</maven.compiler.target>
-
<maven.compiler.source>1.8</maven.compiler.source>
-
<mybatis.version>3.2.7</mybatis.version>
-
<mybatis-spring.version>1.2.2</mybatis-spring.version>
-
</properties>
-
<dependencies>
-
<!--springboot-->
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-security</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-thymeleaf</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.thymeleaf.extras</groupId>
-
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
-
</dependency>
-
<!--db-->
-
<dependency>
-
<groupId>mysql</groupId>
-
<artifactId>mysql-connector-java</artifactId>
-
<version>6.0.5</version>
-
</dependency>
-
<dependency>
-
<groupId>com.mchange</groupId>
-
<artifactId>c3p0</artifactId>
-
<version>0.9.5.2</version>
-
<exclusions>
-
<exclusion>
-
<groupId>commons-logging</groupId>
-
<artifactId>commons-logging</artifactId>
-
</exclusion>
-
</exclusions>
-
</dependency>
-
<!--mybatis-->
-
<dependency>
-
<groupId>org.springframework</groupId>
-
<artifactId>spring-jdbc</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.mybatis</groupId>
-
<artifactId>mybatis</artifactId>
-
<version>${mybatis.version}</version>
-
</dependency>
-
<dependency>
-
<groupId>org.mybatis</groupId>
-
<artifactId>mybatis-spring</artifactId>
-
<version>${mybatis-spring.version}</version>
-
</dependency>
-
</dependencies>
-
</project>
3 数据库设计
3.1 数据库表的设计,登录比较简单,只有五张表,分别是用户表(sys_user),角色表(sys_role),权限表(sys_permission),角色权限关系表(sys_role_user),角色用户关系表(sys_role_permission).
然后我们往里面填充一点数据
-
insert into SYS_USER (id,username, password) values (1,'admin', 'admin');
-
insert into SYS_USER (id,username, password) values (2,'user', 'user');
-
insert into SYS_ROLE(id,name) values(1,'ROLE_ADMIN');
-
insert into SYS_ROLE(id,name) values(2,'ROLE_USER');
-
insert into SYS_ROLE_USER(SYS_USER_ID,ROLES_ID) values(1,1);
-
insert into SYS_ROLE_USER(SYS_USER_ID,ROLES_ID) values(2,2);
-
BEGIN;
-
INSERT INTO `Sys_permission` VALUES ('1', 'ROLE_HOME', 'home', '/', null), ('2', 'ROLE_ADMIN', 'user', '/admin', null);
-
COMMIT;
-
BEGIN;
-
INSERT INTO `Sys_permission_role` VALUES ('1', '1', '1'), ('2', '1', '2'), ('3', '2', '1');
-
COMMIT;
3.2 实体类的创建
3.2.1 SysUser.java
-
package com.example.securitymybatis.entity;
-
import java.util.List;
-
//用户表(sys_user表)
-
public class SysUser {
-
//主键id
-
private Integer id;
-
//用户名
-
private String username;
-
//登录密码
-
private String password;
-
private List<SysRole> roles;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getUsername() {
-
return username;
-
}
-
public void setUsername(String username) {
-
this.username = username;
-
}
-
public String getPassword() {
-
return password;
-
}
-
public void setPassword(String password) {
-
this.password = password;
-
}
-
public List<SysRole> getRoles() {
-
return roles;
-
}
-
public void setRoles(List<SysRole> roles) {
-
this.roles = roles;
-
}
-
}
3.2.2 SysRole.java
-
package com.example.securitymybatis.entity;
-
//角色表(sys_role表)
-
public class SysRole {
-
//主键id
-
private Integer id;
-
//角色名称
-
private String name;
-
public Integer getId() {
-
return id;
-
}
-
public void setId(Integer id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
}
3.2.3 Permission.java
-
package com.example.securitymybatis.entity;
-
//权限表(sys_permission表)
-
public class Permission {
-
//主键id
-
private int id;
-
//权限名称
-
private String name;
-
//权限描述
-
private String descritpion;
-
//授权链接
-
private String url;
-
//父节点id
-
private int pid;
-
//请求方式
-
private String method;
-
public int getId() {
-
return id;
-
}
-
public void setId(int id) {
-
this.id = id;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public String getDescritpion() {
-
return descritpion;
-
}
-
public void setDescritpion(String descritpion) {
-
this.descritpion = descritpion;
-
}
-
public String getUrl() {
-
return url;
-
}
-
public void setUrl(String url) {
-
this.url = url;
-
}
-
public int getPid() {
-
return pid;
-
}
-
public void setPid(int pid) {
-
this.pid = pid;
-
}
-
public String getMethod() {
-
return method;
-
}
-
public void setMethod(String method) {
-
this.method = method;
-
}
-
}
3.2.4 Message.java
-
package com.example.securitymybatis.entity;
-
//中间信息表(无数据库实体表,只用来专递中间信息)
-
public class Message {
-
//信息标题
-
private String title;
-
//信息主体
-
private String content;
-
//额外信息
-
private String etraInfo;
-
public Message(String title, String content, String etraInfo) {
-
super();
-
this.title = title;
-
this.content = content;
-
this.etraInfo = etraInfo;
-
}
-
public String getTitle() {
-
return title;
-
}
-
public void setTitle(String title) {
-
this.title = title;
-
}
-
public String getContent() {
-
return content;
-
}
-
public void setContent(String content) {
-
this.content = content;
-
}
-
public String getEtraInfo() {
-
return etraInfo;
-
}
-
public void setEtraInfo(String etraInfo) {
-
this.etraInfo = etraInfo;
-
}
-
}
4 config配置
4.1 DBconfig.java配置
-
package com.example.securitymybatis.config;
-
import com.mchange.v2.c3p0.ComboPooledDataSource;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.core.env.Environment;
-
import java.beans.PropertyVetoException;
-
//配置数据源
-
@Configuration
-
public class DBconfig {
-
@Autowired
-
private Environment env;
-
@Bean(name="dataSource")
-
public ComboPooledDataSource dataSource() throws PropertyVetoException {
-
ComboPooledDataSource dataSource = new ComboPooledDataSource();
-
dataSource.setDriverClass(env.getProperty("jdbc.db.driverClassName"));
-
dataSource.setJdbcUrl(env.getProperty("jdbc.db.url"));
-
dataSource.setUser(env.getProperty("jdbc.db.username"));
-
dataSource.setPassword(env.getProperty("jdbc.db.password"));
-
dataSource.setMaxPoolSize(20);
-
dataSource.setMinPoolSize(5);
-
dataSource.setInitialPoolSize(10);
-
dataSource.setMaxIdleTime(300);
-
dataSource.setAcquireIncrement(5);
-
dataSource.setIdleConnectionTestPeriod(60);
-
return dataSource;
-
}
-
}
4.2 MybaitsConfig.java配置
-
package com.example.securitymybatis.config;
-
import org.mybatis.spring.SqlSessionFactoryBean;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.ApplicationContext;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.ComponentScan;
-
import org.springframework.context.annotation.Configuration;
-
import javax.sql.DataSource;
-
//扫描mapper文件
-
@Configuration
-
@ComponentScan
-
public class MybatisConfig {
-
@Autowired
-
private DataSource dataSource;
-
@Bean(name = "sqlSessionFactory")
-
public SqlSessionFactoryBean sqlSessionFactory(ApplicationContext applicationContext) throws Exception {
-
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
-
sessionFactory.setDataSource(dataSource);
-
// sessionFactory.setPlugins(new Interceptor[]{new PageInterceptor()});
-
sessionFactory.setMapperLocations(applicationContext.getResources("classpath*:mapper/*.xml"));
-
return sessionFactory;
-
}
-
}
4.3 MybatisScannerConfig.java配置
-
package com.example.securitymybatis.config;
-
import org.mybatis.spring.mapper.MapperScannerConfigurer;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
//扫描dao层文件
-
@Configuration
-
public class MybatisScannerConfig {
-
@Bean
-
public MapperScannerConfigurer MapperScannerConfigurer() {
-
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
-
mapperScannerConfigurer.setBasePackage("com.example.securitymybatis.dao");
-
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
-
return mapperScannerConfigurer;
-
}
-
}
4.4 TransactionConfig.java配置
-
package com.example.securitymybatis.config;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.ComponentScan;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
-
import org.springframework.transaction.PlatformTransactionManager;
-
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
-
import javax.sql.DataSource;
-
//开启事务管理
-
@Configuration
-
@ComponentScan
-
public class TransactionConfig implements TransactionManagementConfigurer {
-
@Autowired
-
private DataSource dataSource;
-
@Bean(name = "transactionManager")
-
@Override
-
public PlatformTransactionManager annotationDrivenTransactionManager() {
-
return new DataSourceTransactionManager(dataSource);
-
}
-
}
4.5 WebMvcConfig.java 配置
-
package com.example.securitymybatis.config;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
-
//web视图管理
-
@Configuration
-
public class WebMvcConfig extends WebMvcConfigurerAdapter {
-
@Override
-
public void addViewControllers(ViewControllerRegistry registry) {
-
registry.addViewController("/login").setViewName("login");
-
}
-
}
-
package com.example.securitymybatis.config;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
-
//web视图管理
-
@Configuration
-
public class WebMvcConfig extends WebMvcConfigurerAdapter {
-
@Override
-
public void addViewControllers(ViewControllerRegistry registry) {
-
registry.addViewController("/login").setViewName("login");
-
}
-
}
4.6 WebSecurityConfig.java 配置(本文核心配置,请看注释)
-
package com.example.securitymybatis.config;
-
import com.example.securitymybatis.security.MyFilterSecurityInterceptor;
-
import com.example.securitymybatis.security.UserService;
-
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.EnableWebSecurity;
-
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
import org.springframework.security.core.userdetails.UserDetailsService;
-
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
-
//业务核心
-
@Configuration
-
@EnableWebSecurity
-
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
-
@Autowired
-
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
-
@Bean
-
UserDetailsService customUserService(){ //注册UserDetailsService 的bean
-
return new UserService();
-
}
-
@Override
-
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-
auth.userDetailsService(customUserService()); //user Details Service验证
-
}
-
@Override
-
protected void configure(HttpSecurity http) throws Exception {
-
// // http.authorizeRequests()每个匹配器按照它们被声明的顺序被考虑。
-
http
-
.authorizeRequests()
-
// 所有用户均可访问的资源
-
.antMatchers("/css/**", "/js/**", "/images/**", "/webjars/**", "**/favicon.ico").permitAll()
-
// ROLE_USER的权限才能访问的资源
-
.antMatchers("/user/**").hasRole("USER")
-
// 任何尚未匹配的URL只需要验证用户即可访问
-
.anyRequest().authenticated()
-
.and()
-
.formLogin()
-
// 指定登录页面,授予所有用户访问登录页面
-
.loginPage("/login")
-
//设置默认登录成功跳转页面,错误回到login界面
-
.defaultSuccessUrl("/index").failureUrl("/login?error").permitAll()
-
.and()
-
//开启cookie保存用户数据
-
.rememberMe()
-
//设置cookie有效期
-
.tokenValiditySeconds(60 * 60 * 24 * 7)
-
//设置cookie的私钥
-
.key("security")
-
.and()
-
.logout()
-
.permitAll();
-
//登录拦截器
-
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)
-
//springsecurity4自动开启csrf(跨站请求伪造)与restful冲突
-
.csrf().disable();
-
}
-
}
完成config后我们开始从自底向上的方式来编写我们的登陆功能。
5 mapper的配置
5.1 UserDaoMapper.xml 配置
-
<?xml version="1.0" encoding="UTF-8" ?>
-
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.example.securitymybatis.dao.UserDao">
-
<resultMap id="userMap" type="com.example.securitymybatis.entity.SysUser">
-
<id property="id" column="ID"/>
-
<result property="username" column="username"/>
-
<result property="password" column="PASSWORD"/>
-
<collection property="roles" ofType="com.example.securitymybatis.entity.SysRole">
-
<result column="name" property="name"/>
-
</collection>
-
</resultMap>
-
<select id="findByUserName" parameterType="String" resultMap="userMap">
-
select u.*
-
,r.name
-
from sys_user u
-
LEFT JOIN sys_role_user sru on u.id= sru.sys_user_id
-
LEFT JOIN sys_role r on sru.sys_role_id=r.id
-
where username= #{username}
-
</select>
-
</mapper>
5.2 PermissionDaoMapper.xml 配置
-
<?xml version="1.0" encoding="UTF-8" ?>
-
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-
<mapper namespace="com.example.securitymybatis.dao.PermissionDao">
-
<select id="findAll" resultType="com.example.securitymybatis.entity.Permission">
-
SELECT * from Sys_permission ;
-
</select>
-
<select id="findByAdminUserId" parameterType="int" resultType="com.example.securitymybatis.entity.Permission">
-
select p.*
-
from sys_user u
-
LEFT JOIN sys_role_user sru on u.id= sru.sys_user_id
-
LEFT JOIN sys_role r on sru.sys_role_id=r.id
-
LEFT JOIN sys_permission_role spr on spr.role_id=r.id
-
LEFT JOIN sys_permission p on p.id =spr.permission_id
-
where u.id=#{userId}
-
</select>
-
</mapper>
6 dao层接口配置
6.1 UserDao.java
-
package com.example.securitymybatis.dao;
-
import com.example.securitymybatis.entity.SysUser;
-
public interface UserDao {
-
public SysUser findByUserName(String username);
-
}
6.2 PermissionDao.java
-
package com.example.securitymybatis.dao;
-
import com.example.securitymybatis.entity.Permission;
-
import java.util.List;
-
public interface PermissionDao {
-
public List<Permission> findAll();
-
public List<Permission> findByAdminUserId(int userId);
-
}
7 serivice 层配置
7.1 UserService.java(其中会用到后面写到的方法)
-
package com.example.securitymybatis.security;
-
import com.example.securitymybatis.dao.PermissionDao;
-
import com.example.securitymybatis.dao.UserDao;
-
import com.example.securitymybatis.entity.Permission;
-
import com.example.securitymybatis.entity.SysUser;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.security.core.GrantedAuthority;
-
import org.springframework.security.core.authority.SimpleGrantedAuthority;
-
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 org.springframework.security.core.userdetails.User;
-
import java.util.ArrayList;
-
import java.util.List;
-
@Service
-
public class UserService implements UserDetailsService { //自定义UserDetailsService 接口
-
@Autowired
-
UserDao userDao;
-
@Autowired
-
PermissionDao permissionDao;
-
@Override
-
public UserDetails loadUserByUsername(String username) { //重写loadUserByUsername 方法获得 userdetails 类型用户
-
SysUser user = userDao.findByUserName(username);
-
if (user != null) {
-
List<Permission> permissions = permissionDao.findByAdminUserId(user.getId());
-
List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
-
for (Permission permission : permissions) {
-
if (permission != null && permission.getName()!=null) {
-
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
-
//1:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。
-
grantedAuthorities.add(grantedAuthority);
-
}
-
}
-
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
-
} else {
-
throw new UsernameNotFoundException("admin: " + username + " do not exist!");
-
}
-
}
-
}
8 controller层配置
8.1 LoginController.java(其中有几个方法的权限是管理员才有的)
-
package com.example.securitymybatis.controller;
-
import com.example.securitymybatis.entity.Message;
-
import org.springframework.stereotype.Controller;
-
import org.springframework.ui.Model;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.ResponseBody;
-
@Controller
-
public class LoginController {
-
@RequestMapping("/index")
-
public String index(Model model){
-
Message msg = new Message("测试标题","测试内容","额外信息,只对管理员显示");
-
model.addAttribute("msg", msg);
-
return "index";
-
}
-
@RequestMapping("/admin")
-
@ResponseBody
-
public String hello(){
-
return "hello admin";
-
}
-
@RequestMapping("/login")
-
public String login(){
-
return "login";
-
}
-
@RequestMapping(value = "/user", method = RequestMethod.GET)
-
@ResponseBody
-
public String getList(){
-
return "hello getList";
-
}
-
@RequestMapping(value = "/user", method = RequestMethod.POST)
-
@ResponseBody
-
public String save(){
-
return "hello save";
-
}
-
@RequestMapping(value = "/user", method = RequestMethod.PUT)
-
@ResponseBody
-
public String update(){
-
return "hello update";
-
}
-
}
9 security 配置(核心)
9.1 MyAccessDecisionManager.java(授权管理器)
-
package com.example.securitymybatis.security;
-
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.Service;
-
import java.util.Collection;
-
import java.util.Iterator;
-
@Service
-
public class MyAccessDecisionManager implements AccessDecisionManager {
-
// decide 方法是判定是否拥有权限的决策方法,
-
//authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
-
//object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
-
//configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
-
@Override
-
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
-
if(null== configAttributes || configAttributes.size() <=0) {
-
return;
-
}
-
ConfigAttribute c;
-
String needRole;
-
for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
-
c = iter.next();
-
needRole = c.getAttribute();
-
for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
-
if(needRole.trim().equals(ga.getAuthority())) {
-
return;
-
}
-
}
-
}
-
throw new AccessDeniedException("no right");
-
}
-
@Override
-
public boolean supports(ConfigAttribute attribute) {
-
return true;
-
}
-
@Override
-
public boolean supports(Class<?> clazz) {
-
return true;
-
}
-
}
9.2 MyFilterSecurityInterceptor.java (自定义拦截器)
-
package com.example.securitymybatis.security;
-
import org.springframework.beans.factory.annotation.Autowired;
-
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.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
-
import org.springframework.stereotype.Service;
-
import javax.servlet.*;
-
import java.io.IOException;
-
@Service
-
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
-
@Autowired
-
private FilterInvocationSecurityMetadataSource securityMetadataSource;
-
@Autowired
-
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
-
super.setAccessDecisionManager(myAccessDecisionManager);
-
}
-
@Override
-
public void init(FilterConfig filterConfig) throws ServletException {
-
}
-
@Override
-
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-
FilterInvocation fi = new FilterInvocation(request, response, chain);
-
invoke(fi);
-
}
-
public void invoke(FilterInvocation fi) throws IOException, ServletException {
-
//fi里面有一个被拦截的url
-
//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
-
//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
-
InterceptorStatusToken token = super.beforeInvocation(fi);
-
try {
-
//执行下一个拦截器
-
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
-
} finally {
-
super.afterInvocation(token, null);
-
}
-
}
-
@Override
-
public void destroy() {
-
}
-
@Override
-
public Class<?> getSecureObjectClass() {
-
return FilterInvocation.class;
-
}
-
@Override
-
public SecurityMetadataSource obtainSecurityMetadataSource() {
-
return this.securityMetadataSource;
-
}
-
}
9.3 MyInvocationSecurityMetadataSourceService.java(获取被拦截url所需的全部权限的方法)
-
package com.example.securitymybatis.security;
-
import com.example.securitymybatis.dao.PermissionDao;
-
import com.example.securitymybatis.entity.Permission;
-
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.security.web.util.matcher.AntPathRequestMatcher;
-
import org.springframework.stereotype.Service;
-
import javax.servlet.http.HttpServletRequest;
-
import java.util.*;
-
@Service
-
public class MyInvocationSecurityMetadataSourceService implements
-
FilterInvocationSecurityMetadataSource {
-
@Autowired
-
private PermissionDao permissionDao;
-
private HashMap<String, Collection<ConfigAttribute>> map =null;
-
/**
-
* 加载权限表中所有权限
-
*/
-
public void loadResourceDefine(){
-
map = new HashMap<>();
-
Collection<ConfigAttribute> array;
-
ConfigAttribute cfg;
-
List<Permission> permissions = permissionDao.findAll();
-
for(Permission permission : permissions) {
-
array = new ArrayList<>();
-
cfg = new SecurityConfig(permission.getName());
-
//此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
-
array.add(cfg);
-
//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
-
map.put(permission.getUrl(), array);
-
}
-
}
-
//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
-
@Override
-
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
-
if(map ==null) loadResourceDefine();
-
//object 中包含用户请求的request 信息
-
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
-
AntPathRequestMatcher matcher;
-
String resUrl;
-
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
-
resUrl = iter.next();
-
matcher = new AntPathRequestMatcher(resUrl);
-
if(matcher.matches(request)) {
-
return map.get(resUrl);
-
}
-
}
-
return null;
-
}
-
@Override
-
public Collection<ConfigAttribute> getAllConfigAttributes() {
-
return null;
-
}
-
@Override
-
public boolean supports(Class<?> clazz) {
-
return true;
-
}
-
}
如上所示security的拦截器就配好了,如果有看不懂的地方,可在网上搜security拦截器,我这里就不一一详细解释了。
10 界面代码
10.1 login.html
-
<!DOCTYPE html>
-
<html xmlns:th="http://www.thymeleaf.org">
-
<head>
-
<meta content="text/html;charset=UTF-8"/>
-
<title>登录页面</title>
-
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
-
<style type="text/css">
-
body {
-
padding-top: 50px;
-
}
-
.starter-template {
-
padding: 40px 15px;
-
text-align: center;
-
}
-
.form-group input{
-
margin: auto;
-
width: 550px;
-
}
-
</style>
-
</head>
-
<body>
-
<nav class="navbar navbar-inverse navbar-fixed-top">
-
<div class="container">
-
<div class="navbar-header">
-
<a class="navbar-brand" href="#">Spring Security演示</a>
-
</div>
-
<div id="navbar" class="collapse navbar-collapse">
-
<ul class="nav navbar-nav">
-
<li><a th:href="@{/}"> 首页 </a></li>
-
</ul>
-
</div><!--/.nav-collapse -->
-
</div>
-
</nav>
-
<div class="container">
-
<div class="starter-template">
-
<p th:if="${param.logout}" class="bg-warning">已成功注销</p><!-- 1 -->
-
<p th:if="${param.error}" class="bg-danger">有错误,请重试</p> <!-- 2 -->
-
<h2>使用账号密码登录</h2>
-
<form name="form" th:action="@{/login}" action="/login" method="POST"> <!-- 3 -->
-
<div class="form-group">
-
<label for="username">账号</label>
-
<input type="text" class="form-control" name="username" value="" placeholder="账号" />
-
</div>
-
<div class="form-group">
-
<label for="password">密码</label>
-
<input type="password" class="form-control" name="password" placeholder="密码" />
-
</div>
-
<input type="submit" id="login" value="Login" class="btn btn-primary" />
-
</form>
-
</div>
-
</div>
-
</body>
-
</html>
10.2 index.html
-
<!DOCTYPE html>
-
<html xmlns:th="http://www.thymeleaf.org"
-
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
-
<head>
-
<meta content="text/html;charset=UTF-8"/>
-
<title sec:authentication="name"></title>
-
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}" />
-
<style type="text/css">
-
body {
-
padding-top: 50px;
-
}
-
.starter-template {
-
padding: 40px 15px;
-
text-align: center;
-
}
-
</style>
-
</head>
-
<body>
-
<nav class="navbar navbar-inverse navbar-fixed-top">
-
<div class="container">
-
<div class="navbar-header">
-
<a class="navbar-brand" href="#">Spring Security演示</a>
-
</div>
-
<div id="navbar" class="collapse navbar-collapse">
-
<ul class="nav navbar-nav">
-
<li><a th:href="@{/}"> 首页 </a></li>
-
<li><a th:href="@{/admin}"> admin </a></li>
-
</ul>
-
</div><!--/.nav-collapse -->
-
</div>
-
</nav>
-
<div class="container">
-
<div class="starter-template">
-
<h1 th:text="${msg.title}"></h1>
-
<p class="bg-primary" th:text="${msg.content}"></p>
-
<div sec:authorize="hasRole('ROLE_ADMIN')"> <!-- 用户类型为ROLE_ADMIN 显示 -->
-
<p class="bg-info" th:text="${msg.etraInfo}"></p>
-
</div>
-
<div sec:authorize="hasRole('ROLE_ADMIN')"> <!-- 用户类型为ROLE_ADMIN 显示 -->
-
<p class="bg-info">恭喜您,您有 ROLE_ADMIN 权限 </p>
-
</div>
-
<form th:action="@{/logout}" method="post">
-
<input type="submit" class="btn btn-primary" value="注销"/>
-
</form>
-
</div>
-
</div>
-
</body>
-
</html>
11 整体结构和运行效果。
整个项目的文件结构如上图所示,static下的css文件夹中只有一个bootstrap.min.css文件,网上都能搜到,我就不上传了。
11.1 运行项目,点击右上角的绿色三角形运行项目,不需要配置tomcat.
11.2 打开浏览器,输入localhost:8080即可进入登录界面
11.3 管理员和普通用户登录显示不同的内容
11.3.1 管理员登录显示
11.3.2 普通用户登录显示
11.4 点击管理员登录后跳转界面上的admin,页面会显示“hello admin”,而普通用户会有403错误。
11.4.1 管理员点击admin
11.4.2 普通用户点击admin
至此,整个springboot+springsecurity+mybaits项目就搭建好了。