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.



Sure, here's an outline of how you can implement the shipping REST APIs using Spring Boot, Spring MVC, JPA/Batis, MySQL, and JWT: 1. Set up a Spring Boot project with the necessary dependencies for Spring MVC, JPA/Batis, MySQL, and JWT. You can use Spring Initializr to generate the project structure and add the dependencies. 2. Define the domain model for the shipping application, including entities such as Order, Product, Customer, and ShippingAddress. Map the entities to database tables using JPA annotations or MyBatis mapper XML files. 3. Implement the repository layer to perform CRUD operations on the database using JPA or MyBatis. You can use Spring Data JPA or MyBatis-Spring to simplify the implementation. 4. Define the REST API endpoints for the shipping application using Spring MVC annotations. Use JWT for authentication and authorization of the API endpoints. 5. Implement the service layer to perform business logic operations such as calculating shipping costs, validating orders, and processing payments. Use dependency injection to inject the repository and other services into the service layer. 6. Write unit tests to ensure that the application logic is working correctly. You can use JUnit and Mockito to write the tests. 7. Deploy the application to a server and test the API endpoints using a tool such as Postman. Here's some example code to get you started: ```java @RestController @RequestMapping("/api/orders") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/") public ResponseEntity<Order> createOrder(@RequestBody Order order) { Order createdOrder = orderService.createOrder(order); return ResponseEntity.ok(createdOrder); } @GetMapping("/{id}") public ResponseEntity<Order> getOrder(@PathVariable("id") Long orderId) { Order order = orderService.getOrder(orderId); return ResponseEntity.ok(order); } // Other API endpoints for updating and deleting orders } @Service public class OrderService { @Autowired private OrderRepository orderRepository; public Order createOrder(Order order) { // Calculate shipping costs and validate the order order.setShippingCosts(10.0); order.setTotalPrice(order.getProducts().stream() .mapToDouble(Product::getPrice) .sum() + order.getShippingCosts()); return orderRepository.save(order); } public Order getOrder(Long orderId) { return orderRepository.findById(orderId) .orElseThrow(() -> new NotFoundException("Order not found")); } // Other service methods for updating and deleting orders } @Repository public interface OrderRepository extends JpaRepository<Order, Long> { } ``` This code defines a REST API endpoint for creating orders and getting orders by ID. The order creation logic is implemented in the OrderService class, which calculates shipping costs and saves the order to the database using the OrderRepository interface. JWT authentication and authorization can be added to the API endpoints using Spring Security.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值