【晓时代】SpringBoot + Jpa + Security 简单实例

零、项目结构

图片.png

一、创建数据库

图片.png

二、创建项目

1、基础配置
图片.png

2、选择基础依赖
图片.png

3、引入 LomBok 依赖,让开发跑的飞起

		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <scope>provided</scope>
		</dependency>

4、去掉 mysql 依赖的 scope 标签
图片.png

5、完整依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</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-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
<!-- 			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions> -->
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
		    <groupId>org.projectlombok</groupId>
		    <artifactId>lombok</artifactId>
		    <scope>provided</scope>
		</dependency>

三、修改application.properties 文件名为 application.yml 并写入如下配置

server:
  port: 8081
spring:
  datasource:
    #mysql驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver 
    #数据库连接地址
    url: jdbc:mysql://127.0.0.1:3306/db_spring_security_tutorial?serverTimezone=Asia/Shanghai&autoReconnect=true
    #数据库账号
    username: root
    #密码
    password: 123456
  jpa:
    hibernate:
      #自动建表
      ddl-auto: update
    #方言
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    #显示sql语句
    show-sql: true

四、创建实体类,并创建 UserRepository.java

1、Permission.java

@Entity
@Table(name = "permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	long id;
	String permissionName;	//权限名称
	
	public Permission(String permissionName){
		this.permissionName = permissionName;
	}
}

2、Role.java

@Entity
@Table(name = "role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	long id;
	String roleName;	//角色名称
	@OneToMany(cascade = {CascadeType.ALL} , fetch = FetchType.EAGER)
	@JoinColumn(name = "role_id")
	List<Permission> permissions;
}

3、User.java

@Entity
@Table(name = "user")
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	long id;
	String userName;	//账号
	String password;	//密码
	String salt;		//盐
	@OneToMany(cascade = {CascadeType.ALL} , fetch = FetchType.EAGER)
	@JoinColumn(name = "user_id")
	List<Role> roles;
}

4、UserRepository.java

@Repository
public interface UserRepository extends JpaRepository<User, Long>{
	User findByUserName(String username);
}

五、更改账号验证方式,自定义UserDetailsService

–创建 TemplateUserDetailsService.java

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

import org.springframework.beans.factory.annotation.Autowired;
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 com.negen.entity.Permission;
import com.negen.entity.Role;
import com.negen.entity.User;
import com.negen.repository.UserRepository;
@Service
public class TemplateUserDetailsService implements UserDetailsService{
	@Autowired
	UserRepository userRepository;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User loginUser = userRepository.findByUserName(username);
		if (null == loginUser) {
			//账号不存在,抛出异常
			throw new UsernameNotFoundException(username);
		} else {
			//用户存在,创建 SimpleGrantedAuthority集合
			List<SimpleGrantedAuthority> authorities = 
					new ArrayList<SimpleGrantedAuthority>();
			//遍历角色
			for(Role role:loginUser.getRoles()) {
				//遍历权限
				for(Permission permission:role.getPermissions()) {
					//根据权限名称创建 SimpleGrantedAuthority
					SimpleGrantedAuthority authority = 
							new SimpleGrantedAuthority(permission.getPermissionName());
					authorities.add(authority);
				}
			}
			return new org.springframework.security.core.userdetails.User(
					username,	//用户名
					loginUser.getPassword(),	//用户密码
					authorities		//权限集合
					);
		}
	}
}

六、自定义配置类

1、创建 TemplateWebSecurityConfig.java 并继承 WebSecurityConfigurerAdapter
2、重写两个 configure

图片.png

3、完整代码 TemplateWebSecurityConfig.java 如下:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import com.negen.repository.UserRepository;
import com.negen.service.impl.TemplateUserDetailsService;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TemplateWebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	UserRepository userRepository;
	@Autowired
	TemplateUserDetailsService templateUserDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authenticationProvider());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.cors().and().csrf().disable().authorizeRequests()
				.antMatchers("/user/register",
						"/swagger*//**",
						"/v2/api-docs",
						"/webjars*//**").permitAll()    //过滤 swagger2		
				.anyRequest().authenticated()    //配置所有除上面以为的所有请求必须认证(登录)后才能访问
				.and()
				.formLogin()
				.loginPage("/user/login")
				.loginProcessingUrl("/login")    //登录接口地址
				.successHandler(authenticationSuccessHandler())  //登录成功处理
				.failureHandler(authenticationFailureHandler())  //登录失败处理
				.permitAll();
	}

	// 密码加密方式
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	public DaoAuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
		provider.setHideUserNotFoundExceptions(false); // 设置是否隐藏 UserNotFoundException
		provider.setUserDetailsService(templateUserDetailsService);
		provider.setPasswordEncoder(passwordEncoder());
		return provider;
	}

	// 认证成功处理
	@Bean
	public AuthenticationSuccessHandler authenticationSuccessHandler() {
		// 认证(登录)成功
		return new AuthenticationSuccessHandler() {
			@Override
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
					Authentication authentication) throws IOException, ServletException {
				response.setContentType("application/json;charset=utf-8");
				PrintWriter out = response.getWriter();
				out.write("登录成功");
				out.flush();
			}
		};
	}

	// 认证失败处理
	@Bean
	public AuthenticationFailureHandler authenticationFailureHandler() {
		return new AuthenticationFailureHandler() {

			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException exception) throws IOException, ServletException {
				response.setContentType("application/json;charset=utf-8");
				PrintWriter out = response.getWriter();
				if (exception instanceof UsernameNotFoundException) {
					// 账号不存在
					out.write("账号不存在");
					out.flush();
					return;
				}
				// 密码错误
				out.write("密码错误");
				out.flush();
			}
		};
	}
}

七、创建测试类新增一条用户记录

1、UserTest.java 完整代码如下:

import java.util.ArrayList;
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.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.test.context.junit4.SpringRunner;

import com.negen.entity.Permission;
import com.negen.entity.Role;
import com.negen.entity.User;
import com.negen.repository.UserRepository;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
	@Autowired
	UserRepository userRepository;
	
	@Test
	public void testAddUser() {
		List<Role> roles = new ArrayList<Role>();
		List<Permission> permissions = new ArrayList<Permission>();
		User user = new User();
		Role role = new Role();
		Permission p1 = new Permission("create");
		Permission p2 = new Permission("delete");
		permissions.add(p1);
		permissions.add(p2);
		role.setRoleName("admin");
		role.setPermissions(permissions);
		roles.add(role);
		user.setUserName("Negen");
		user.setPassword(new BCryptPasswordEncoder().encode("123456"));
		user.setRoles(roles);
		userRepository.save(user);
		System.out.println("====>添加用户成功");
	}
}

2、运行单元测试添加一位用户

八、测试

打开postman进行登录接口测试
1、账号不存在
图片.png
2、密码错误
图片.png
3、登录成功
图片.png

九、示例下载地址

https://github.com/Negen9527/spring-security-tutorial-template

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的示例: 1. 后端使用Spring Boot搭建,创建一个名为"login"的Spring Boot项目。 2. 在项目中添加依赖: ```xml <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- MySQL Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- JSON Web Token --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> </dependencies> ``` 3. 创建一个名为"User"的实体类,用于表示用户信息。 ```java @Entity @Table(name = "users") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; @Column(nullable = false) private String role; // getters and setters @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Arrays.asList(new SimpleGrantedAuthority(role)); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } ``` 4. 创建一个名为"UserRepository"的接口,用于操作用户数据。 ```java @Repository public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); } ``` 5. 创建一个名为"JwtUtils"的工具类,用于生成和验证JSON Web Token。 ```java public class JwtUtils { private static final String SECRET_KEY = "secret"; public static String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); claims.put("username", userDetails.getUsername()); claims.put("role", userDetails.getAuthorities().stream().findFirst().get().getAuthority()); return Jwts.builder() .setClaims(claims) .setSubject(userDetails.getUsername()) .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); } public static boolean validateToken(String token, UserDetails userDetails) { String username = getUsernameFromToken(token); return username.equals(userDetails.getUsername()) && !isTokenExpired(token); } public static String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject(); } public static boolean isTokenExpired(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration().before(new Date()); } } ``` 6. 创建一个名为"JwtAuthenticationFilter"的过滤器,用于处理登录请求并生成JSON Web Token。 ```java public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; setFilterProcessesUrl("/api/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { User user = new ObjectMapper().readValue(request.getInputStream(), User.class); return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), Collections.emptyList())); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { String token = JwtUtils.generateToken((UserDetails) authResult.getPrincipal()); response.setContentType("application/json"); response.getWriter().write("{\"token\": \"" + token + "\"}"); } } ``` 7. 创建一个名为"SecurityConfig"的配置类,用于配置Spring Security。 ```java @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserRepository userRepository; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(username -> userRepository.findByUsername(username)).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/login").permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .addFilter(new JwtAuthorizationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } } ``` 8. 创建一个名为"JwtAuthorizationFilter"的过滤器,用于验证请求中的JSON Web Token。 ```java public class JwtAuthorizationFilter extends BasicAuthenticationFilter { private static final String HEADER_STRING = "Authorization"; private static final String TOKEN_PREFIX = "Bearer "; public JwtAuthorizationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader(HEADER_STRING); if (header != null && header.startsWith(TOKEN_PREFIX)) { String token = header.substring(TOKEN_PREFIX.length()); try { String username = JwtUtils.getUsernameFromToken(token); UserDetails userDetails = userDetailsService().loadUserByUsername(username); if (JwtUtils.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (JwtException e) { SecurityContextHolder.clearContext(); } } chain.doFilter(request, response); } } ``` 9. 前端使用Vue.js搭建,创建一个名为"login"的Vue.js项目。 10. 在项目中安装axios和vue-router: ``` npm install --save axios vue-router ``` 11. 创建一个名为"Login"的组件,用于渲染登录表单。 ```vue <template> <div> <h1>Login</h1> <form @submit.prevent="login"> <div> <label for="username">Username:</label> <input type="text" id="username" v-model="username"> </div> <div> <label for="password">Password:</label> <input type="password" id="password" v-model="password"> </div> <div> <button type="submit">Login</button> </div> </form> </div> </template> <script> import axios from 'axios' export default { data () { return { username: '', password: '' } }, methods: { login () { axios.post('/api/login', { username: this.username, password: this.password }) .then(response => { localStorage.setItem('token', response.data.token) this.$router.push('/') }) .catch(error => { console.error(error) }) } } } </script> ``` 12. 创建一个名为"App"的组件,用于渲染主页面。 ```vue <template> <div> <h1>Home</h1> <div v-if="authenticated"> <p>Welcome, {{ username }}!</p> <button @click="logout">Logout</button> </div> <div v-else> <router-link to="/login">Login</router-link> </div> </div> </template> <script> import axios from 'axios' export default { data () { return { authenticated: false, username: '' } }, created () { axios.get('/api/user') .then(response => { this.authenticated = true this.username = response.data.username }) .catch(error => { console.error(error) }) }, methods: { logout () { localStorage.removeItem('token') this.$router.push('/') location.reload() } } } </script> ``` 13. 创建一个名为"router"的路由配置文件,用于配置Vue.js路由。 ```js import Vue from 'vue' import VueRouter from 'vue-router' import Login from './components/Login.vue' import App from './components/App.vue' Vue.use(VueRouter) const router = new VueRouter({ mode: 'history', routes: [ { path: '/', component: App }, { path: '/login', component: Login } ] }) router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } }) export default router ``` 14. 在"index.html"中添加Vue.js和axios的CDN链接: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> </head> <body> <div id="app"></div> <script src="dist/build.js"></script> </body> </html> ``` 15. 在"main.js"中创建Vue.js实例并挂载到DOM上: ```js import Vue from 'vue' import App from './components/App.vue' import router from './router' new Vue({ el: '#app', router, render: h => h(App) }) ``` 16. 运行项目: ``` mvn spring-boot:run ``` ``` npm run dev ``` 现在你可以在浏览器中访问"http://localhost:8080"来测试登录功能了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值