1. 程式人生 > >springboot+spring security+mybaits實現登入許可權管理

springboot+spring security+mybaits實現登入許可權管理

序:

本文采用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專案就搭建好了。

相關推薦

springboot+spring security+mybaits實現登入許可權管理

序:本文采用springboot+spring security+mybatis來解決登入許可權管理的問題。由於是新人,所以在操作方面講得比較詳細。話不多說,直接來開始我們的專案。1 建立springboot+spring security+mybatis 專案這裡我們採用了

SpringBoot+Spring Security Oauth2實現客戶端授權

框架使用SpringBoot 1.5 + Spring Security Oauth2 主要完成了客戶端授權 可以通過mysql資料庫 將客戶端與token資訊儲存在資料庫中。 每次授權會將新的token儲存在mysql中,進行客戶端驗證時,先會從資料庫中查詢客

(一)如何使用Spring-security實現登入驗證功能(XML配置方式)?

先從使用xml的方式來實現使用者的許可權登入 (1)需要在maven工程中加上關於spring-secutity的jar包的依賴 //spring-securityd 有關的依賴 <

SpringBoot+Spring Security無法實現跨域解決辦法

未使用Security時跨域: import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframewor

springBoot整合spring security+JWT實現單點登入許可權管理前後端分離--築基中期

## 寫在前面 在前一篇文章當中,我們介紹了springBoot整合spring security單體應用版,在這篇文章當中,我將介紹springBoot整合spring secury+JWT實現單點登入與許可權管理。 本文涉及的許可權管理模型是**基於資源的動態許可權管理**。資料庫設計的表有 user

Springboot整合Security實現登入許可權驗證

緒:              通過這個查,參照別人的demo實現了security許可權認證,可能有些地方寫的不對,供參考!希望對你有幫助 一.配置     1.pom配置 <?xml version="1.0" encoding="UTF-8"?>

(二)如何使用spring-security實現使用者的登入許可權功能?(配合使用資料庫的方式)

如何使用spring-security來實現使用者的登入功能之配合使用資料庫的方式 這個圖大家先熟悉一下簡單的過一遍,等把步驟都寫完之後,後面會總結 (一)使用spring-security之前需要做的準備(基於springMVC和dubbo的專案)

jwt,spring security ,feign,zuul,eureka 前後端分離 整合 實現 簡單 許可權管理系統 與 使用者認證的實現

本例子 包含有 一下模組:  eureka 模組,充當 註冊中心角色 user-service-api 客戶端介面和實體 user-service-provider 客戶端服務提供者 user-conusmer 客戶端消費者 zuul 模組為閘道器,用來實現統一路由

Spring Boot Security + MyBatis 實現登入的安全控制機制

篇幅有限,前端的頁面程式碼就不贅述了,直接分享後端邏輯程式碼: 1.Maven專案物件依賴檔案Pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.or

spring security + thymeleaf 判斷登入使用者的許可權

spring security的UserDetailService是我自己定義的。 @Component public class MyUserDetailsService implements UserDetailsService {@Autowired private

SpringCloud+SpringBoot+OAuth2+Spring Security+Redis實現的微服務統一認證授權

因為目前做了一個基於Spring Cloud的微服務專案,所以瞭解到了OAuth2,打算整合一下OAuth2來實現統一認證。關於OAuth是一個關於授權的開放網路標準,目前的版本是2.0,這裡我就不多做介紹了。下面貼一下我學習過程中參考的資料。 理解OAuth 2.0——阮一峰 Spring

Spring security 自定義登入許可權控制

一、先說必要的配置檔案: 1、web.xml檔案新增上 <!-- Spring Security 許可權框架 --> <filter> <filter-name>springSecurityFilterChain</filt

spring-security4.2實現登入退出以及許可權配置

最近用到了spring-security框架來實現登入驗證。 以前做登入的步驟是: 1、使用者輸入使用者名稱、密碼登入 2、連線資料庫對使用者名稱、密碼進行驗證 3、獲取使用者資訊(角色列表等等) 4、獲取相關操作許可權 security安全框架有

SpringBoot通過自己的配置檔案或者從資料庫spring security動態配置url許可權

我使用springboot的時候想做自己的配置檔案的,用不了xml就重寫了過濾器 首先需要了解spring security內建的各種filter: Alias Filter Class Namespace Element or Attribute CHANNEL

SpringBoot + Spring Security 學習筆記實現短信驗證碼+登錄功能

pass lsa nproc 驗證 過期 ant chan oci 功能 在 Spring Security 中基於表單的認證模式,默認就是密碼帳號登錄認證,那麽對於短信驗證碼+登錄的方式,Spring Security 沒有現成的接口可以使用,所以需要自己的封裝一個類似的

基於spring-security-oauth2實現單點登入(持續更新)

##基於spring-security-實現資料庫版## 文章程式碼地址:[連結描述][1]可以下載直接執行,基於springb

【手摸手,帶你搭建前後端分離商城系統】03 整合Spring Security token 實現方案,完成主業務登入

## 【手摸手,帶你搭建前後端分離商城系統】03 整合Spring Security token 實現方案,完成主業務登入 上節裡面,我們已經將基本的前端 `VUE + Element UI` 整合到了一起。並且通過 `axios` 傳送請求到後端API。 解決跨域問題後、成功從後端獲取到資料。 本小結

Spring security csrf實現前端純html+ajax

var light urn span 同時 pan mode eth res spring security集成csrf進行post等請求時,為了防止csrf攻擊,需要獲取token才能訪問 因此需要添加 <input type="hidden" name="${_

Spring Security的使用(訪問許可權控制)

訪問許可權控制 粗粒度:對一個功能的訪問進行控制 細粒度:對該功能下的資料顯示進行控制 注意:許可權控制,需要在spring-mvc.xml中配置,否則會導致失效 <aop:aspectj-autoproxy proxy-target-class="true"></a

Spring Security簡單實現自定義退出功能

1.前端頁面寫法 <a href="javascript:;" onclick="logoutBackground()">退出</a> 2.js /** * 退出後臺 */ function logoutBackground() { $.get("/