spring boot 第三步 mvc+jpa+spring security+themeleaf

1.实现目的:通过spring boot security ,jpa,mvc,themeleaf实现用户登录权限验证

2.pom.xml引入依赖及application.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>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>myspringboot2</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</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>
		    <version>3.0.2.RELEASE</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/world
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update


spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

3.准备数据库,以下分别为用户表,角色表,菜单权限表,用户角色关系表,角色菜单权限关系表:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `username` varchar(32) DEFAULT NULL COMMENT '用户名',
  `password` varchar(128) DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8

CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `name` varchar(64) DEFAULT NULL COMMENT '角色名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

CREATE TABLE `menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `name` varchar(64) DEFAULT NULL COMMENT '菜单或按键名称',
  `url` varchar(500) DEFAULT NULL COMMENT '访问url',
  `permission` varchar(500) DEFAULT NULL COMMENT '权限(ROLE_前缀)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

CREATE TABLE `user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `role_id` int(11) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

CREATE TABLE `role_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `role_id` int(11) DEFAULT NULL COMMENT '角色id',
  `menu_id` int(11) DEFAULT NULL COMMENT '菜单id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8

3.对就上面五个表的实体类:

package com.example.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 用户表
 */
@Entity
@Table(name="user")
public class User {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@Column(name = "username")
	private String username;
	
	@Column(name = "password")
	private String password;


	public Long getId() {
		return id;
	}


	public void setId(Long 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;
	}

}

package com.example.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;



/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 权限表
 */
@Entity
@Table(name = "role")
public class Role {
	
	
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@Column(name="name")
	private String name;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}


package com.example.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 菜单表
 */
@Entity
@Table(name = "menu")
public class Menu {
	
	
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	
	@Column(name="name")
	private String name;
	
	@Column(name="url")
	private String url;
	
	@Column(name="permission")
	private String permission;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getPermission() {
		return permission;
	}

	public void setPermission(String permission) {
		this.permission = permission;
	}

}

package com.example.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 用户角色表
 */
@Entity
@Table(name="user_role")
public class UserRole {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@Column(name = "user_id")
	private Long userId;
	
	@Column(name = "role_id")
	private Long roleId;


	public Long getId() {
		return id;
	}


	public void setId(Long id) {
		this.id = id;
	}


	public Long getUserId() {
		return userId;
	}


	public void setUserId(Long userId) {
		this.userId = userId;
	}


	public Long getRoleId() {
		return roleId;
	}


	public void setRoleId(Long roleId) {
		this.roleId = roleId;
	}

}

package com.example.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 权限菜单表
 */
@Entity
@Table(name="role_menu")
public class RoleMenu {
	
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	
	@Column(name="role_id")
	private Long roleId;
	
	@Column(name="menu_id")
	private Long menuId;


	public Long getId() {
		return id;
	}


	public void setId(Long id) {
		this.id = id;
	}


	public Long getRoleId() {
		return roleId;
	}


	public void setRoleId(Long roleId) {
		this.roleId = roleId;
	}


	public Long getMenuId() {
		return menuId;
	}


	public void setMenuId(Long menuId) {
		this.menuId = menuId;
	}

}

4.对应DAO 及Service方法

package com.example.jpa.dao;
import java.io.Serializable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description DAO基类
 */
@NoRepositoryBean
public interface BaseDao<T,ID extends Serializable> extends JpaRepository<T, ID> {

}

package com.example.jpa.dao;
import org.springframework.stereotype.Repository;
import com.example.jpa.entity.User;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 用户DAO
 */
@Repository
public interface UserDao extends BaseDao<User, Long>{
	User findOneByUsername(String name);
}

package com.example.jpa.service;
import com.example.jpa.entity.User;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 描述
 */
public interface UserService {
	void addUser(User u);
	User findByName(String name);
}

package com.example.jpa.service;
import java.util.List;
import com.example.jpa.entity.Menu;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 描述
 */
public interface MenuService {
	List<Menu> findByUserName(String username);
}

package com.example.jpa.service.impl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.jpa.dao.UserDao;
import com.example.jpa.entity.User;
import com.example.jpa.service.UserService;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 描述
 */
@Service("userService")
@Transactional(readOnly=true)
public class UserServiceImpl implements UserService {
	
	
	@Autowired
	private UserDao userDao;

	@Override
	@Transactional
	public void addUser(User u) {
		userDao.save(u);
	}

	@Override
	public User findByName(String name) {
		return userDao.findOneByUsername(name);
	}
}


package com.example.jpa.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.jpa.dao.MenuDao;
import com.example.jpa.entity.Menu;
import com.example.jpa.service.MenuService;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 描述
 */
@Service("menuService")
@Transactional(readOnly=true)
public class MenuServiceImpl implements MenuService {
	
	@Autowired
	private MenuDao menuDao;

	@Override
	public List<Menu> findByUserName(String username) {
		// TODO Auto-generated method stub
		return menuDao.findByUsername(username);
	}

}

5. spring boot 的junit测试,controller也可以用junit测试,小伙伴们可以自己偿试微笑

package com.example.jpatest;

import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.jpa.entity.Menu;
import com.example.jpa.entity.User;
import com.example.jpa.service.MenuService;
import com.example.jpa.service.UserService;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
@ComponentScan(basePackages = "com.example.jpa.*")
public class UserRespositoryUnitTest {
	
        @Autowired
	private UserService userService;
	
	@Autowired
	private MenuService menuService;
	
	
	/**
	 * 新增测试用户数据
	 */
	@Test
	@Rollback(false)//为了可以用junit新增一些测试数据,如果不加这个注解,默认是会回滚的。
	public void addUser(){
		
		User u = new User();
		u.setUsername("test");
//		u.setPassword(Md5Utils.md5("123456"));
		//直接可以在spring框架引用bcrypt加盐加密,相比MD5加密,bcrypt加密每次的密码后台显示都是唯一的,加密性更加强。
		u.setPassword(BCrypt.hashpw("123456", BCrypt.gensalt()));
		userService.addUser(u);
		
	}
	
	/**
	 * 测试菜单权限方法
	 */
	@Test
	public void findByUsernamePermisions(){
	    List<Menu> menus =menuService.findByUserName("zzm");
	    System.out.println("menus>>>>>>>>>>>>>>>"+menus);
	}

}

5.编写spring security 配置类,spring 中可以直接引用BCrypt加密,也可以自己定义选择加密方式匹配自己原系统的用户密码加密方法。

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 配置security权限验证 
 */
@Configuration
public class BrowSecurityConfig extends WebSecurityConfigurerAdapter{
	
	
	
	/**
	 * 定义登录入口
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// TODO Auto-generated method stub
		 http.formLogin()                    //  定义当需要用户登录时候,转到的登录页面。
         .loginPage("/user/login")           // 设置登录页面
         .loginProcessingUrl("/user/dologin")  // 自定义的登录接口
         //.defaultSuccessUrl("/user/dologin")
         .and()
         .authorizeRequests()        // 定义哪些URL需要被保护、哪些不需要被保护
         .antMatchers("/user/login").permitAll()     // 设置所有人都可以访问登录页面
         .anyRequest()               // 任何请求,登录后可以访问
         .authenticated()
         .and()
         .csrf().disable();          // 关闭csrf防护
	}
	
	
	/**
	 * bcry加密验证,可以重写用自己定义的加密方法
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		 
	     return new BCryptPasswordEncoder();
	     
	     //return new MyMd5PasswordEncoder(); 自定义的md5数据加密
	 }

}

6.实现spring security 安全验证UserdetailsService接口:

package com.example.config;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.User;
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.Component;

import com.example.jpa.entity.Menu;
import com.example.jpa.service.MenuService;
import com.example.jpa.service.UserService;


/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 实现security 的  UserDetailsService接口,引入自己的后台权限数据库
 */

@Component
public class MyUserDetailService implements UserDetailsService{

    private Logger logger = LoggerFactory.getLogger(getClass());
	
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private MenuService menuService;
	
    
    
    /**
     * spring security 安全验证用户方法
     */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		com.example.jpa.entity.User u =  userService.findByName(username);//自己系统的用户类
		User user  = null;
		if(null==u){
			 throw new UsernameNotFoundException("找不到该账户信息!");
		}
		else{
			//spring security 管理的用户类,只需要将用户名,密码,及角色列表数据交给spring就可以了
			 user = new User(username, u.getPassword(), findPermisions(username));
		}

		return user;
	}
	
	
	/**
	 * 根据用户名去确定用户拥有菜单权限
	 * @param username
	 * @return
	 */
	public List<GrantedAuthority> findPermisions(String username){
		List<GrantedAuthority> list = new ArrayList<>();
		List<Menu> menus =   menuService.findByUserName(username);
		for (Menu menu : menus) {
			list.add(new SimpleGrantedAuthority(menu.getPermission()));
		}
		return list;
	}

}

7.实现的controller

package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.jpa.entity.User;

/**
 * 
 * @author 钟之鸣
 * @time   2018年7月11日
 * @description 定义一个普通用户登录及登录验证的类
 */
@Controller
@RequestMapping("/user")
public class UserController {
	

	/**
	 * 登录入口
	 * @param user
	 * @return
	 */
	@RequestMapping("/login")
	public String login(User user){
		return "/user/login";
	}
	
	
	/**
	 * 登录后
	 * @return
	 */
	@RequestMapping("/dologin")
	public String dologin(){
		return "/user/test";
	}

}

8.themeleaf简单登录页面,详细标签请自行参考开发文档:

登录页面:

<!DOCTYPE>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
   <h2>自定义登录页面</h2>
    
    <form  th:action="@{/user/dologin}"  th:method="post" th:object="${user}">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" th:field="*{username}"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" th:field="*{password}"></td>
            </tr>
            <tr>
                <td colspan="2"><button type="submit">登录</button></td>
            </tr>
        </table>
    </form>
</body>
</html>
登录成功后测试权限标签的页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
 <p>登录成功后测试页面!</p>
 <p>你拥有的权限如下:</p><br>
 <div sec:authorize="hasRole('ROLE_TEST_1')">菜单1</div>
 <div sec:authorize="hasRole('ROLE_TEST_2')">菜单2</div>
 <div sec:authorize="hasRole('ROLE_TEST_3')">菜单3</div>
</body>
</html>

这里要注意POM一定要引入springsecurity4依赖,否则sec:authorize标签无效。


9.实现结果,根据用户登录控制显示菜单权限,整个代码结构如下:


10.



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值