Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。它的核心是一组过滤器链,不同的功能经由不同的过滤器。
Apache Shiro 是 Java 的一个安全框架,对比 Spring Security,可能没有 Spring Security 做的功能强大
package com.zkb.config;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsService myUserDetailsService;
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建token验证表,第一次启动服务会自动创建表,第二次重启会报该表已存在,所以这里注释掉
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
//2.先调用这个配置方法装载配置
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutUrl("/logout")
.logoutSuccessUrl("/login").permitAll();
http.exceptionHandling().accessDeniedPage("/noauth.html");
http.formLogin()
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll() //这里是登录成功之后,会跳转到哪里
.and().authorizeRequests()
.antMatchers("/", "/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证,其它的路径都会跳转到认证页面
.antMatchers("/zkb/index").hasAuthority("admins")
.anyRequest().authenticated()
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60) //设置有效时长,单位秒
.userDetailsService(myUserDetailsService)
.and().csrf().disable(); //关闭csrf防护
}
}
package com.zkb.controller;
import com.zkb.entity.Users;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/zkb")
public class ZkbController {
@GetMapping("/index")
public String login() {
return "hello index";
}
@GetMapping("/update")
@PreAuthorize("hasAnyAuthority('admins')")
@PostAuthorize("hasAnyAuthority('admin')")
public String update() {
System.out.println("update.....");
return "hello update";
}
@RequestMapping("getAll")
@PreAuthorize("hasRole('admins')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<Users> getAllUser(){
ArrayList<Users> list = new ArrayList<>();
list.add(new Users(1,"admin1","6666"));
list.add(new Users(2,"admin2","888"));
return list;
}
}
package com.zkb.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private Integer id;
private String username;
private String password;
}
package com.zkb.mapper;
import com.zkb.entity.Users;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UsersMapper extends BaseMapper<Users> {
}
package com.zkb.service;
import com.zkb.entity.Users;
import com.zkb.mapper.UsersMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<Users> queryWrapper = new QueryWrapper<Users>();
queryWrapper.lambda().eq(Users::getUsername, username);
Users users = usersMapper.selectOne(queryWrapper);
//判断
if (null == users) {
//数据库没有用户名,认证失败
throw new UsernameNotFoundException("用户名不存在!");
}
queryWrapper.lambda().eq(Users::getPassword, users.getPassword());
Users usersByPassword = usersMapper.selectOne(queryWrapper);
//判断
if (usersByPassword == null) {
throw new UsernameNotFoundException("密码错误!");
}
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admins, Role_sale, ROLE_manager");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()), authorities);
}
}
package com.zkb;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@MapperScan("com.zkb.mapper")
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecuritydemoApplication {
public static void main(String[] args) {
SpringApplication.run(SecuritydemoApplication.class, args);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="text" name="password"/><br/>
<input type="checkbox" name="remember-me"/>
自动登录<br/>
<button type="submit">登录</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>没有访问权限!</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录成功<a href="/logout">退出</a></h1>
</body>
</html>
server:
port: 8001
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
url: jdbc:mysql://127.0.0.1:3306/springbootjpa?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zkb</groupId>
<artifactId>securitydemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>securitydemo</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users` (
`id` bigint(20) NOT NULL,
`username` varchar(30) DEFAULT NULL,
`password` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
这里也可以取消记录我
package com.zkb.config;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsService myUserDetailsService;
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建token验证表,第一次启动服务会自动创建表,第二次重启会报该表已存在,所以这里注释掉
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
//2.先调用这个配置方法装载配置
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutUrl("/logout")
.logoutSuccessUrl("/login").permitAll();
http.exceptionHandling().accessDeniedPage("/noauth.html");
http.formLogin()
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll() //这里是登录成功之后,会跳转到哪里
.and().authorizeRequests()
.antMatchers("/", "/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证,其它的路径都会跳转到认证页面
.antMatchers("/zkb/index").hasAuthority("admins")
// .anyRequest().authenticated()
// .and().rememberMe().tokenRepository(persistentTokenRepository())
// .tokenValiditySeconds(60) //设置有效时长,单位秒
// .userDetailsService(myUserDetailsService)
.and().csrf().disable(); //关闭csrf防护
}
}
可以正常登录是吧
当然这里是Security自带的登录入口,也可以自定义登录入口哈哈
试一下,这里是我们自定义的登录入口
package com.zkb.config;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UserDetailsService myUserDetailsService;
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建token验证表,第一次启动服务会自动创建表,第二次重启会报该表已存在,所以这里注释掉
//jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
//2.先调用这个配置方法装载配置
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutUrl("/logout")
.logoutSuccessUrl("/login").permitAll();
http.exceptionHandling().accessDeniedPage("/noauth.html");
http.formLogin()
.loginPage("/login.html") //登录页面设置
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll() //这里是登录成功之后,会跳转到哪里
.and().authorizeRequests()
.antMatchers("/", "/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证,其它的路径都会跳转到认证页面
.antMatchers("/zkb/index").hasAuthority("admins")
.anyRequest().authenticated()
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60) //设置有效时长,单位秒
.userDetailsService(myUserDetailsService)
.and().csrf().disable(); //关闭csrf防护
}
}
等等等等,打完收工