├── .gitignore ├── README.md ├── SpringSecurity ├── .gitignore ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── cn │ │ │ └── geekview │ │ │ ├── SpringsecurityApplication.java │ │ │ ├── config │ │ │ ├── MVCConfig.java │ │ │ └── SecurityConfig.java │ │ │ ├── controller │ │ │ └── HomeController.java │ │ │ ├── entity │ │ │ ├── model │ │ │ │ ├── Msg.java │ │ │ │ ├── SysRole.java │ │ │ │ └── SysUser.java │ │ │ └── repository │ │ │ │ └── SysUserRepository.java │ │ │ └── service │ │ │ └── CustomUserService.java │ └── resources │ │ ├── application.properties │ │ ├── static │ │ └── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── signin.css │ │ └── templates │ │ ├── index.html │ │ └── login.html │ └── test │ └── java │ └── cn │ └── geekview │ └── SpringsecurityApplicationTests.java ├── init_sql ├── SpringSecurity.sql ├── SpringSecurity2.sql └── SpringSecurity4.sql ├── springsecurity2 ├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── cn │ │ │ └── geekview │ │ │ ├── RequestMappingHandlerConfig.java │ │ │ ├── Springsecurity2Application.java │ │ │ ├── config │ │ │ ├── LoginSuccessHandler.java │ │ │ ├── MvcConfig.java │ │ │ ├── MyFilterSecurityInterceptor.java │ │ │ └── WebSecurityConfig.java │ │ │ ├── controller │ │ │ └── BasicController.java │ │ │ ├── entity │ │ │ ├── model │ │ │ │ ├── SysResource.java │ │ │ │ ├── SysResourceRole.java │ │ │ │ ├── SysRole.java │ │ │ │ └── SysUser.java │ │ │ └── repository │ │ │ │ ├── SysResourceRepository.java │ │ │ │ ├── SysRoleRepository.java │ │ │ │ └── SysUserRepository.java │ │ │ └── service │ │ │ ├── CustomAccessDecisionManager.java │ │ │ ├── CustomInvocationSecurityMetadataSourceService.java │ │ │ └── CustomUserDetailsService.java │ └── resources │ │ ├── application.properties │ │ └── templates │ │ ├── admin.html │ │ ├── deny.html │ │ ├── home.html │ │ ├── login.html │ │ ├── super.html │ │ ├── user.html │ │ └── vip.html │ └── test │ └── java │ └── cn │ └── geekview │ ├── BasicTest.java │ └── Springsecurity2ApplicationTests.java ├── springsecurity3 ├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── cn │ │ │ └── geekview │ │ │ ├── Springsecurity3Application.java │ │ │ ├── annotation │ │ │ ├── EmailValidator.java │ │ │ └── ValidEmail.java │ │ │ ├── config │ │ │ └── WebSecurityConfig.java │ │ │ ├── controller │ │ │ ├── IndexController.java │ │ │ ├── RegistrationController.java │ │ │ └── UserController.java │ │ │ ├── entity │ │ │ ├── model │ │ │ │ ├── Resource.java │ │ │ │ ├── Role.java │ │ │ │ ├── User.java │ │ │ │ └── VerificationToken.java │ │ │ └── repository │ │ │ │ ├── UserRepository.java │ │ │ │ └── VerificationTokenRepository.java │ │ │ ├── event │ │ │ ├── Listener │ │ │ │ └── RegistrationListener.java │ │ │ └── OnRegistrationCompleteEvent.java │ │ │ └── service │ │ │ ├── IUserService.java │ │ │ └── impl │ │ │ └── UserServiceImpl.java │ └── resources │ │ ├── application.properties │ │ └── templates │ │ ├── emailError.html │ │ ├── home.html │ │ ├── login.html │ │ └── register.html │ └── test │ └── java │ └── cn │ └── geekview │ └── Springsecurity3ApplicationTests.java └── springsecurity4 ├── .gitignore ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── cn │ │ └── geekview │ │ ├── Springsecurity4Application.java │ │ ├── annotation │ │ ├── EmailCheck.java │ │ └── EmailChek.java │ │ ├── config │ │ └── security │ │ │ ├── AccessDecisionManagerImpl.java │ │ │ ├── CSRFSecurityRequestMatcher.java │ │ │ ├── FilterInvocationSecurityMetadataSourceImpl.java │ │ │ └── WebSecurityConfig.java │ │ ├── controller │ │ ├── IndexController.java │ │ └── UserController.java │ │ ├── domain │ │ ├── dto │ │ │ └── RegistraFormDTO.java │ │ ├── entity │ │ │ ├── Resource.java │ │ │ ├── Role.java │ │ │ ├── User.java │ │ │ └── ValidateToken.java │ │ └── repository │ │ │ ├── ResourceRepository.java │ │ │ ├── RoleRepository.java │ │ │ ├── UserRepository.java │ │ │ └── ValidateTokenRepository.java │ │ ├── event │ │ ├── OnRegistrationCompleteEvent.java │ │ └── listener │ │ │ └── RegistrationListener.java │ │ ├── handler │ │ ├── LoginFailureHandler.java │ │ └── LoginSuccessHandler.java │ │ ├── service │ │ ├── IUserService.java │ │ └── impl │ │ │ └── UserServiceImpl.java │ │ └── util │ │ └── ValidateCode.java └── resources │ ├── application.properties │ └── templates │ ├── CSRFTest.html │ ├── admin.html │ ├── deny.html │ ├── home.html │ ├── login.html │ ├── regist.html │ ├── super.html │ ├── user.html │ └── vip.html └── test └── java └── cn └── geekview └── Springsecurity4ApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | /.idea 3 | target/ 4 | /springsecurity2/.mvn 5 | /springsecurity3/.mvn 6 | /springsecurity4/.mvn 7 | !.mvn/wrapper/maven-wrapper.jar 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | build/ 26 | nbbuild/ 27 | dist/ 28 | nbdist/ 29 | .nb-gradle/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot 2 | 此工程主要用于学习SpringBoot相关知识 3 | 4 | 目前新增了四个模块: 5 | 6 | 1、SpringSecurity: 用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置。 7 | 2、SpringSecurity2: 用户、角色、权限和资源均采用数据库存储 8 | 3、SpringSecurity3:用户注册、邮箱验证 9 | 4、SpringSecurity4:注册登录合并 10 | 11 | 注:每个模块里面的README文件有对应模块的简要说明,时间关系,可能写的比较简陋一些,多担待,有问题请提issue 12 | 13 | 14 | 思考与总结: 15 | 16 | 1、将资源、角色、用户全部分开保存在数据库,再建立中间表进行管理处理,这样操作比较麻烦: 17 | **在使用前,需要先配置角色可以访问那些资源的中间表**,可以自己写代码处理, 18 | 比较简单一点的方式就是: 19 | **只对用户进行角色的区分,具体哪一个角色具备哪些权限,由注解来决定:@PreAuthorize("hasRole('ROLE')"**) 20 | 这样在Controller的路径下添加注解,就只需要管理用户和角色了, 21 | 这样的缺陷是: 22 | 如果资源对应的角色需要修改的时候需要重新修改代码; 23 | 如果想直接根据用户的角色显示对应的内容,当然也可以处理,又要单独实现一圈, 24 | 25 | -------------------------------------------------------------------------------- /SpringSecurity/.gitignore: -------------------------------------------------------------------------------- 1 | /.mvn 2 | /mvnw 3 | /mvnw.cmd 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | 27 | ### Java ### 28 | target/ 29 | -------------------------------------------------------------------------------- /SpringSecurity/README.md: -------------------------------------------------------------------------------- 1 | # SpringSecurity 2 | SpringBoot和SpringSecurity整合方式2: 3 | 4 | 用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置。 5 | -------------------------------------------------------------------------------- /SpringSecurity/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cn.geekview 7 | springsecurity 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | springsecurity 12 | Demo project for Spring Boot With Spring Security 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-redis 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-mail 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-security 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-thymeleaf 47 | 48 | 49 | org.thymeleaf.extras 50 | thymeleaf-extras-springsecurity4 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-web 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-devtools 59 | runtime 60 | 61 | 62 | mysql 63 | mysql-connector-java 64 | runtime 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-test 69 | test 70 | 71 | 72 | org.springframework.security 73 | spring-security-test 74 | test 75 | 76 | 77 | org.springframework.social 78 | spring-social-core 79 | 80 | 81 | org.springframework.social 82 | spring-social-config 83 | 84 | 85 | org.springframework.social 86 | spring-social-web 87 | 88 | 89 | org.springframework.social 90 | spring-social-security 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/SpringsecurityApplication.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringsecurityApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringsecurityApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/config/MVCConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 6 | 7 | /** 8 | * MVC配置类 9 | * 这个可以直接进行跳转,不需要经过Controller 10 | */ 11 | @Configuration 12 | public class MVCConfig extends WebMvcConfigurerAdapter{ 13 | 14 | @Override 15 | public void addViewControllers(ViewControllerRegistry registry) { 16 | registry.addViewController("/login").setViewName("login"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import cn.geekview.service.CustomUserService; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | 12 | /** 13 | * Spring Security 配置类 14 | */ 15 | @Configuration 16 | @EnableWebSecurity 17 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 18 | 19 | @Bean 20 | UserDetailsService customUserService(){ 21 | return new CustomUserService(); 22 | } 23 | 24 | @Override 25 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 26 | auth.userDetailsService(customUserService()); 27 | } 28 | 29 | /** 30 | * 定义哪些URL需要被保护 31 | * @param http 32 | * @throws Exception 33 | */ 34 | @Override 35 | protected void configure(HttpSecurity http) throws Exception { 36 | http.authorizeRequests() 37 | .anyRequest().authenticated() 38 | .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll().and() 39 | .logout().permitAll(); 40 | } 41 | 42 | 43 | 44 | /** 45 | * 46 | * @param auth 47 | * @throws Exception 48 | */ 49 | // @Override 50 | // protected void configure(AuthenticationManagerBuilder auth) throws Exception { 51 | // //将单个用户设置在内存中 52 | //// super.configure(auth);//根据默认实现获取一个AuthenticationManager---》调用WebSecurityConfigurerAdapter的authenticationManager()方法 53 | // // 这里加入内存有哪些用户,页面登录就可以输入哪些用户登录 54 | // auth.inMemoryAuthentication().withUser("admin").password("password").roles("USER"); 55 | // 56 | // } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/controller/HomeController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import cn.geekview.entity.model.Msg; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | @Controller 9 | public class HomeController { 10 | @RequestMapping("/") 11 | public String index(Model model) { 12 | Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示"); 13 | model.addAttribute("msg", msg); 14 | return "index"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/entity/model/Msg.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | public class Msg { 4 | private String title; 5 | private String content; 6 | private String extraInfo; 7 | 8 | public Msg() { 9 | } 10 | 11 | public String getTitle() { 12 | return title; 13 | } 14 | 15 | public void setTitle(String title) { 16 | this.title = title; 17 | } 18 | 19 | public String getContent() { 20 | return content; 21 | } 22 | 23 | public void setContent(String content) { 24 | this.content = content; 25 | } 26 | 27 | public String getExtraInfo() { 28 | return extraInfo; 29 | } 30 | 31 | public void setExtraInfo(String extraInfo) { 32 | this.extraInfo = extraInfo; 33 | } 34 | 35 | public Msg(String title, String content, String extraInfo) { 36 | this.title = title; 37 | this.content = content; 38 | this.extraInfo = extraInfo; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/entity/model/SysRole.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class SysRole { 9 | 10 | @Id 11 | @GeneratedValue 12 | private Long id; 13 | 14 | private String name; 15 | 16 | public Long getId() { 17 | return id; 18 | } 19 | 20 | public void setId(Long id) { 21 | this.id = id; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public void setName(String name) { 29 | this.name = name; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/entity/model/SysUser.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | import javax.persistence.*; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.List; 11 | 12 | /** 13 | * 14 | * 实现UserDetails接口,这样我们的用户实体即为Spring Security所使用的用户 15 | * 16 | */ 17 | 18 | @Entity 19 | public class SysUser implements UserDetails{ 20 | @Id 21 | @GeneratedValue 22 | private Long id; 23 | private String username;//不能写成userName 24 | private String password;//不能写成passWord 25 | 26 | @ManyToMany(cascade = CascadeType.REFRESH,fetch = FetchType.EAGER) 27 | private List roles; 28 | 29 | public Long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Long id) { 34 | this.id = id; 35 | } 36 | 37 | @Override 38 | public String getUsername() { 39 | return username; 40 | } 41 | 42 | public void setUsername(String username) { 43 | this.username = username; 44 | } 45 | 46 | @Override 47 | public String getPassword() { 48 | return password; 49 | } 50 | 51 | public void setPassword(String password) { 52 | this.password = password; 53 | } 54 | 55 | public List getRoles() { 56 | return roles; 57 | } 58 | 59 | public void setRoles(List roles) { 60 | this.roles = roles; 61 | } 62 | 63 | @Override 64 | public boolean isAccountNonExpired() { 65 | return true; 66 | } 67 | 68 | @Override 69 | public boolean isAccountNonLocked() { 70 | return true; 71 | } 72 | 73 | @Override 74 | public boolean isCredentialsNonExpired() { 75 | return true; 76 | } 77 | 78 | @Override 79 | public boolean isEnabled() { 80 | return true; 81 | } 82 | 83 | /* 84 | 正常情况下,角色和权限是两回事,所以我们还需要重写getAuthorities方法,将用户的角色和权限关联起来 85 | */ 86 | @Override 87 | public Collection getAuthorities() { 88 | List authorities = new ArrayList<>(); 89 | List roles = this.roles; 90 | for (SysRole role : roles) { 91 | authorities.add(new SimpleGrantedAuthority(role.getName())); 92 | } 93 | return authorities; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/entity/repository/SysUserRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.SysUser; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface SysUserRepository extends JpaRepository { 7 | SysUser findByUsername(String username); 8 | } 9 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/java/cn/geekview/service/CustomUserService.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import cn.geekview.entity.model.SysUser; 4 | import cn.geekview.entity.repository.SysUserRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class CustomUserService implements UserDetailsService { 13 | 14 | @Autowired 15 | private SysUserRepository userRepository; 16 | 17 | @Override 18 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 19 | SysUser user = userRepository.findByUsername(username); 20 | if (user==null){ 21 | throw new UsernameNotFoundException("用户名不存在"); 22 | } 23 | System.out.println("username:"+username); 24 | System.out.println("username:"+user.getUsername()+";password:"+user.getPassword()); 25 | return user; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScnuWang/SpringBoot/dab0a4c95389a69f3dbaadb09d28741e4d706c66/SpringSecurity/src/main/resources/application.properties -------------------------------------------------------------------------------- /SpringSecurity/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 | 33 |
34 |
35 |

36 |

37 |
38 |

39 |
40 |
41 |

无更多显示信息

42 |
43 |
44 | 45 |
46 |
47 |
48 | 49 | -------------------------------------------------------------------------------- /SpringSecurity/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 7 | 8 | 17 | 18 | 19 | 32 |
33 |
34 |

已注销

35 |

有错误,请重试

36 |

使用账号密码登录

37 | 48 |
49 |
50 | 51 | -------------------------------------------------------------------------------- /SpringSecurity/src/test/java/cn/geekview/SpringsecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringsecurityApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /init_sql/SpringSecurity.sql: -------------------------------------------------------------------------------- 1 | insert into `sys_role`(`id`,`name`) values (1,'ROLE_USER'),(2,'ROLE_VIP'),(3,'ROLE_ADMIN'),(4,'ROLE_SUPER_ADMIN'); 2 | insert into `sys_user`(`id`,`password`,`username`) values (1,'user','user'),(2,'vip','vip'),(3,'admin','admin'),(4,'sadmin','sadmin'); 3 | insert into `sys_user_roles`(`sys_user_id`,`roles_id`) values (1,1),(2,2),(3,3),(4,4); -------------------------------------------------------------------------------- /init_sql/SpringSecurity2.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `spring_boot`.`sys_user` (`id`, `password`, `username`) VALUES ('1', '$2a$04$5EzhLxOthTOMc5pN/Ozg1.wzrbijftcgEqXPouJt7igKIoxAmVWtO', 'liuyan'); 2 | INSERT INTO `spring_boot`.`sys_user` (`id`, `password`, `username`) VALUES ('2', '$2a$04$2/GN1BIPc6agihyHMQce4OSr34CgPCTRgiTQYEr51Wc1zm7u5eOTm', 'dongdong'); 3 | INSERT INTO `spring_boot`.`sys_user` (`id`, `password`, `username`) VALUES ('3', '$2a$04$3Cksa3X0bYdYDkDm231w/uZ8BWJiV57OKTG9PZo8iFHTxNZ1jT77a', 'admin'); 4 | INSERT INTO `spring_boot`.`sys_user` (`id`, `password`, `username`) VALUES ('4', '$2a$04$3Cksa3X0bYdYDkDm231w/uZ8BWJiV57OKTG9PZo8iFHTxNZ1jT77a', 'jason'); 5 | 6 | INSERT INTO `spring_boot`.`sys_role` (`id`, `role_name`) VALUES ('1', 'USER'); 7 | INSERT INTO `spring_boot`.`sys_role` (`id`, `role_name`) VALUES ('2', 'ADMIN'); 8 | INSERT INTO `spring_boot`.`sys_role` (`id`, `role_name`) VALUES ('3', 'VIP'); 9 | INSERT INTO `spring_boot`.`sys_role` (`id`, `role_name`) VALUES ('4', 'SUPER'); 10 | 11 | 12 | INSERT INTO `spring_boot`.`sys_resource` (`id`, `resource_name`) VALUES ('1', '/vip'); 13 | INSERT INTO `spring_boot`.`sys_resource` (`id`, `resource_name`) VALUES ('2', '/user'); 14 | INSERT INTO `spring_boot`.`sys_resource` (`id`, `resource_name`) VALUES ('3', '/admin'); 15 | INSERT INTO `spring_boot`.`sys_resource` (`id`, `resource_name`) VALUES ('4', '/super'); 16 | 17 | 18 | INSERT INTO `spring_boot`.`sys_user_roles` (`sys_user_id`, `roles_id`) VALUES ('3', '2'); 19 | INSERT INTO `spring_boot`.`sys_user_roles` (`sys_user_id`, `roles_id`) VALUES ('1', '3'); 20 | INSERT INTO `spring_boot`.`sys_user_roles` (`sys_user_id`, `roles_id`) VALUES ('2', '1'); 21 | INSERT INTO `spring_boot`.`sys_user_roles` (`sys_user_id`, `roles_id`) VALUES ('4', '4'); 22 | 23 | 24 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('1', '2', '1'); 25 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('2', '1', '3'); 26 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('3', '3', '2'); 27 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('4', '1', '2'); 28 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('5', '4', '4'); 29 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('6', '2', '4'); 30 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('7', '3', '4'); 31 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('8', '1', '4'); 32 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('9', '2', '2'); 33 | INSERT INTO `spring_boot`.`sys_resource_role` (`id`, `resource_id`, `role_id`) VALUES ('10', '2', '3'); 34 | 35 | CREATE TABLE persistent_logins ( 36 | username VARCHAR (64) NOT NULL, 37 | series VARCHAR (64) PRIMARY KEY, 38 | token VARCHAR (64) NOT NULL, 39 | last_used TIMESTAMP NOT NULL 40 | ) -------------------------------------------------------------------------------- /init_sql/SpringSecurity4.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `spring_boot`.`t_role` (`id`, `role_name`) VALUES ('1', 'USER'); 2 | INSERT INTO `spring_boot`.`t_role` (`id`, `role_name`) VALUES ('2', 'ADMIN'); 3 | INSERT INTO `spring_boot`.`t_role` (`id`, `role_name`) VALUES ('3', 'VIP'); 4 | INSERT INTO `spring_boot`.`t_role` (`id`, `role_name`) VALUES ('4', 'SUPER'); 5 | 6 | 7 | INSERT INTO `spring_boot`.`t_resource` (`id`, `resource`) VALUES ('1', '/vip'); 8 | INSERT INTO `spring_boot`.`t_resource` (`id`, `resource`) VALUES ('2', '/user'); 9 | INSERT INTO `spring_boot`.`t_resource` (`id`, `resource`) VALUES ('3', '/admin'); 10 | INSERT INTO `spring_boot`.`t_resource` (`id`, `resource`) VALUES ('4', '/super'); 11 | 12 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('1', '2'); 13 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('1', '3'); 14 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('3', '2'); 15 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('2', '1'); 16 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('4', '2'); 17 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('4', '3'); 18 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('4', '1'); 19 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('2', '2'); 20 | INSERT INTO `spring_boot`.`t_role_resources` (`roles_id` , `resources_id` ) VALUES ('3', '2'); 21 | 22 | CREATE TABLE persistent_logins ( 23 | username VARCHAR (64) NOT NULL, 24 | series VARCHAR (64) PRIMARY KEY, 25 | token VARCHAR (64) NOT NULL, 26 | last_used TIMESTAMP NOT NULL 27 | ); 28 | 29 | 30 | INSERT INTO `spring_boot`.`t_user_roles` (`users_id`, `roles_id`) VALUES ('3', '2'); 31 | INSERT INTO `spring_boot`.`t_user_roles` (`users_id`, `roles_id`) VALUES ('1', '3'); 32 | INSERT INTO `spring_boot`.`t_user_roles` (`users_id`, `roles_id`) VALUES ('2', '1'); 33 | INSERT INTO `spring_boot`.`t_user_roles` (`users_id`, `roles_id`) VALUES ('4', '4'); -------------------------------------------------------------------------------- /springsecurity2/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /springsecurity2/README.md: -------------------------------------------------------------------------------- 1 | # SpringSecurity 2 | SpringBoot和SpringSecurity整合方式2: 3 | 4 | 细分角色和权限,并将用户、角色、权限和资源均采用数据库存储, 5 | 并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器 6 | 并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService, 7 | 并在配置文件中进行相应配置。 8 | 9 | 测试账号: 10 | 11 | admin/123456 12 | liuyan/123456 13 | dongdong/123456 14 | jason/123456 15 | 16 | 操作步骤: 17 | 18 | 1、使用IDEA(也可以用其他类似工具如:Eclipse)直接下载安装工程 19 | 2、修改数据库相关信息 20 | 3、启动**SpringSecurity2**模块 21 | 4、初始化init_sql目录里面的SpringSecurity2.sql初始化语句 22 | 5、访问localhost,然后使用上面的测试账号登录 23 | 24 | 25 | 需求1: 26 | 27 | 登录成功后默认跳转到对应展示的页面 28 | admin---》/admin 29 | vip---》/vip 30 | user---》/user 31 | super---》/super 32 | 访问其他权限的页面,会提示权限不足 33 | 测试通过 34 | 35 | 需求2: 36 | 37 | super登陆后可以访问/super、/admin、/vip、/user 38 | admin登录后可以访问/admin、/vip、/user 39 | vip登录后可以访问/vip、/user 40 | user登录后可以访问/user 41 | 测试通过 42 | 43 | 需求3: 44 | 45 | Cookie快速登录 46 | 47 | 测试通过 48 | 49 | 目前缺陷1:~~资源和角色一一对应~~ 50 | 51 | 目前缺陷2:~~Remenber Me持久化到数据库没有实现~~ 52 | 53 | 目前缺陷3:~~前端获取不到权限验证失败的信息(异常信息)~~ 54 | 55 | 56 | -------------------------------------------------------------------------------- /springsecurity2/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /springsecurity2/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /springsecurity2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cn.geekview 7 | springsecurity2 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | springsecurity2 12 | Demo project for Spring Boot With Spring Security 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-security 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-thymeleaf 39 | 40 | 41 | mysql 42 | mysql-connector-java 43 | runtime 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | org.springframework.security 52 | spring-security-test 53 | test 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-autoconfigure 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/RequestMappingHandlerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 6 | 7 | @Configuration 8 | public class RequestMappingHandlerConfig { 9 | @Bean 10 | public RequestMappingHandlerMapping requestMappingHandlerMapping() { 11 | RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); 12 | return mapping; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/Springsecurity2Application.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.web.method.HandlerMethod; 8 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 9 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 10 | 11 | import javax.annotation.PostConstruct; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | @SpringBootApplication 17 | @EnableAutoConfiguration //注意如果:MyFilterSecurityInterceptor.class 18 | //@EnableAutoConfiguration(exclude = MyFilterSecurityInterceptor.class) // 没有才装配,有就以个人装配为准 19 | public class Springsecurity2Application { 20 | 21 | @Autowired 22 | private RequestMappingHandlerConfig requestMappingHandlerConfig; 23 | 24 | public static void main(String[] args) { 25 | SpringApplication.run(Springsecurity2Application.class, args); 26 | } 27 | 28 | // 获取资源路径 : 只能获取Controller上面的路径,通过继承WebMvcConfigurerAdapter的类添加的路径映射,不能获取 29 | @PostConstruct 30 | public void detectresource(){ 31 | final RequestMappingHandlerMapping requestMappingHandlerMapping = requestMappingHandlerConfig.requestMappingHandlerMapping (); 32 | Map map = requestMappingHandlerMapping.getHandlerMethods(); 33 | Set mappings = map.keySet(); 34 | Map reversedMap = new HashMap(); 35 | for(RequestMappingInfo info : mappings) { 36 | HandlerMethod method = map.get(info); 37 | String methodstr = method.toString(); 38 | methodstr = methodstr.split("\\(")[0]; 39 | 40 | String urlparm = info.getPatternsCondition().toString(); 41 | String url = urlparm.substring(1, urlparm.length()-1); 42 | System.out.println("方法路径:"+methodstr); 43 | System.out.println("请求相对路径:"+url); 44 | // 数据库操作 45 | // List list = sresourceService.findByResourceString(url); 46 | // if(list==null || list.size()<=0){ 47 | // int num = (int)(Math.random()*100+1); 48 | // String rand = String.valueOf(num); 49 | // String resourceId = "res"+System.currentTimeMillis()+rand; 50 | // SysResource sysresource = new SysResource(); 51 | // sysresource.setResourceString(url); 52 | // sysresource.setRemark("0"); 53 | // sysresource.setResourceId(resourceId); 54 | // sysresource.setMethodPath(methodstr); 55 | // sresourceService.save(sysresource); 56 | // System.out.println ("===>"+url); 57 | // } 58 | 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/config/LoginSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.ServletException; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import cn.geekview.entity.model.SysUser; 10 | import cn.geekview.entity.repository.SysResourceRepository; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.GrantedAuthority; 14 | import org.springframework.security.core.context.SecurityContextHolder; 15 | import org.springframework.security.web.DefaultRedirectStrategy; 16 | import org.springframework.security.web.RedirectStrategy; 17 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 18 | 19 | /** 20 | * 登录成功处理器 21 | * 需求: 22 | * 登录成功后,默认跳转到对应角色下的页面 23 | */ 24 | public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { 25 | 26 | @Autowired 27 | private SysResourceRepository resourceRepository; 28 | 29 | private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 30 | 31 | @Override 32 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, 33 | ServletException { 34 | System.out.println(authentication); 35 | 36 | System.out.println(authentication.getPrincipal());//用户对象 37 | //获得授权后可得到用户信息 38 | SysUser user = (SysUser)authentication.getPrincipal(); 39 | 40 | //输出登录提示信息 41 | System.out.println("用户名: " + user.getUsername()); 42 | 43 | System.out.println("用户密码: " + authentication.getCredentials()); 44 | 45 | System.out.println("角色列表:"+authentication.getAuthorities()); 46 | 47 | System.out.println("IP信息 :"+authentication.getDetails()); 48 | 49 | System.out.println("是否被授权 :"+authentication.isAuthenticated()); 50 | 51 | //登录成功后跳转到默认对应的页面 52 | String targetUrl = "/home"; 53 | for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { 54 | String roleName = grantedAuthority.getAuthority(); 55 | switch (roleName){ 56 | case "SUPER": targetUrl = "/super";break; 57 | case "ADMIN": targetUrl = "/admin";break; 58 | case "VIP": targetUrl = "/vip";break; 59 | case "USER": targetUrl = "/user";break; 60 | } 61 | } 62 | redirectStrategy.sendRedirect(request,response,targetUrl); 63 | 64 | // System.out.println("----->"+resourceRepository.findByRoleName(userDetails.getRoles().get(0).getRoleName()).get(0).getResourceName()); 65 | //登录成功后跳转到默认对应的页面 66 | // response.sendRedirect(resourceRepository.findByRoleName(userDetails.getRoles().get(0).getRoleName()).get(0).getResourceName()); 67 | // super.onAuthenticationSuccess(request, response, authentication); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/config/MvcConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 6 | 7 | //@Configuration 8 | //public class MvcConfig extends WebMvcConfigurerAdapter { 9 | // 10 | // @Override 11 | // public void addViewControllers(ViewControllerRegistry registry) { 12 | // registry.addViewController("/home").setViewName("home"); 13 | // registry.addViewController("/").setViewName("home"); 14 | // registry.addViewController("/admin").setViewName("admin"); 15 | // registry.addViewController("/user").setViewName("user"); 16 | // registry.addViewController("/vip").setViewName("vip"); 17 | // registry.addViewController("/login").setViewName("login"); 18 | // registry.addViewController("/super").setViewName("super"); 19 | // registry.addViewController("/deny").setViewName("deny"); 20 | // } 21 | // 22 | //} 23 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/config/MyFilterSecurityInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import cn.geekview.service.CustomAccessDecisionManager; 4 | import cn.geekview.service.CustomInvocationSecurityMetadataSourceService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.access.SecurityMetadataSource; 7 | import org.springframework.security.access.intercept.AbstractSecurityInterceptor; 8 | import org.springframework.security.access.intercept.InterceptorStatusToken; 9 | import org.springframework.security.authentication.AuthenticationManager; 10 | import org.springframework.security.web.FilterInvocation; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.annotation.PostConstruct; 14 | import javax.servlet.*; 15 | import java.io.IOException; 16 | 17 | /** 18 | * 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。 19 | * securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。 20 | * 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中, 21 | * 供Spring Security使用,用于权限校验。 22 | * @author sparta 11/3/29 23 | * 24 | */ 25 | //@Component 26 | //public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{ 27 | // 28 | // @Autowired 29 | // private CustomInvocationSecurityMetadataSourceService mySecurityMetadataSource; 30 | // 31 | // @Autowired 32 | // private CustomAccessDecisionManager myAccessDecisionManager; 33 | // 34 | // /** 35 | // * 在WebSecurityConfig类中configureGlobal(AuthenticationManagerBuilder auth)已经创建 36 | // */ 37 | // @Autowired 38 | // private AuthenticationManager authenticationManager; 39 | // /** 40 | // * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次, 41 | // * 被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。 42 | // */ 43 | // @PostConstruct 44 | // public void init(){ 45 | // super.setAuthenticationManager(authenticationManager); 46 | // super.setAccessDecisionManager(myAccessDecisionManager); 47 | // } 48 | // 49 | // public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{ 50 | // FilterInvocation filterInvocation = new FilterInvocation( request, response, chain ); 51 | // invoke(filterInvocation); 52 | // } 53 | // 54 | // 55 | // public Class getSecureObjectClass(){ 56 | // return FilterInvocation.class; 57 | // } 58 | // 59 | // 60 | // public void invoke( FilterInvocation filterInvocation ) throws IOException, ServletException{ 61 | // System.out.println("MyFilterSecurityInterceptor --> invoke.........................."); 62 | // InterceptorStatusToken token = super.beforeInvocation(filterInvocation); 63 | // try{ 64 | // filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse()); 65 | // }finally{ 66 | // super.afterInvocation(token, null); 67 | // } 68 | // } 69 | // 70 | // 71 | // @Override 72 | // public SecurityMetadataSource obtainSecurityMetadataSource(){ 73 | // System.out.println("MyFilterSecurityInterceptor 中的 obtainSecurityMetadataSource()方法"); 74 | // return this.mySecurityMetadataSource; 75 | // } 76 | // 77 | // public void destroy(){ 78 | // System.out.println("filter===========================end"); 79 | // } 80 | // public void init( FilterConfig filterconfig ) throws ServletException{ 81 | // System.out.println("filter==========================="); 82 | // } 83 | //} 84 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import cn.geekview.service.CustomAccessDecisionManager; 4 | import cn.geekview.service.CustomInvocationSecurityMetadataSourceService; 5 | import cn.geekview.service.CustomUserDetailsService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.security.authentication.AuthenticationManager; 11 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 12 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 13 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 14 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 15 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 16 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 17 | import org.springframework.security.web.access.AccessDeniedHandlerImpl; 18 | import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; 19 | import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; 20 | import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; 21 | import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices; 22 | import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; 23 | 24 | import javax.sql.DataSource; 25 | 26 | 27 | @Configuration 28 | @EnableWebSecurity 29 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 30 | 31 | 32 | @Autowired 33 | private CustomUserDetailsService customUserDetailsService; 34 | 35 | // @Autowired 36 | // private MyFilterSecurityInterceptor myFilterSecurityInterceptor; 37 | 38 | @Override 39 | public AuthenticationManager authenticationManagerBean() throws Exception { 40 | 41 | return super.authenticationManagerBean(); 42 | 43 | } 44 | 45 | @Override 46 | protected void configure(HttpSecurity http) throws Exception { 47 | http 48 | // .addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)//在正确的位置添加我们自定义的过滤器 49 | // 重新添加拥有自己属性的过滤器; 50 | /** 51 | * 不使用自定义过滤器类的话,可以直接使用默认实现的类,并提供自定义的属性 52 | */ 53 | .addFilter(filterSecurityInterceptor()) 54 | .authorizeRequests() 55 | //路径/home不需要验证 56 | .antMatchers("/home").permitAll() 57 | //任何请求都需要授权 58 | .anyRequest().authenticated() 59 | // .expressionHandler(new DefaultWebSecurityExpressionHandler())作用是什么? 60 | .and() 61 | .formLogin() 62 | .loginPage("/login")//之所以加true 是因为 th:if{param.error} 会去读取浏览器地址携带的参数,有了true之后,if就成立,所以后面的th:text就能执行。 63 | .permitAll() 64 | //登录成功处理 65 | .successHandler(loginSuccessHandler()) 66 | .and() 67 | .logout() 68 | .permitAll() 69 | //注销后使session相关信息无效 70 | .invalidateHttpSession(true) 71 | .and() 72 | // 开启rememberme功能:验证,登录成功后,关闭页面,直接访问登陆后可以访问的页面 73 | .rememberMe() 74 | //持久化到数据库 如果不需要持久化到数据库,直接注释掉即可 75 | .rememberMeServices(new PersistentTokenBasedRememberMeServices("MySpringSecurityCookie",customUserDetailsService,persistentTokenRepository())) 76 | //设置有效时间 77 | .tokenValiditySeconds(7*24*60*60); 78 | //权限不足处理 79 | http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl()).accessDeniedPage("/deny"); 80 | } 81 | 82 | @Override 83 | public void configure(WebSecurity web) throws Exception { 84 | super.configure(web); 85 | } 86 | 87 | @Override 88 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 89 | //指定密码加密所使用的加密器为passwordEncoder() 90 | //需要将密码加密后写入数据库 91 | auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder()); 92 | //不删除凭据,以便记住用户 93 | auth.eraseCredentials(false); 94 | } 95 | 96 | /** 97 | * 这种方式注入,会使用AuthenticationManagerBuilder新建一个AuthenticationManager对象,这就和下面 98 | * @Autowired 99 | private AuthenticationManager authenticationManager; 100 | 产生冲突导致报错,所以尽量使用上面的方式引入 101 | 102 | * @return 103 | */ 104 | // @Autowired 105 | // public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 106 | // //指定密码加密所使用的加密器为passwordEncoder() 107 | // //需要将密码加密后写入数据库 108 | // auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder()); 109 | // //不删除凭据,以便记住用户 110 | // auth.eraseCredentials(false); 111 | // 112 | // } 113 | 114 | @Bean 115 | public BCryptPasswordEncoder passwordEncoder() { 116 | return new BCryptPasswordEncoder(4); 117 | } 118 | 119 | @Bean 120 | public LoginSuccessHandler loginSuccessHandler(){ 121 | return new LoginSuccessHandler(); 122 | } 123 | 124 | @Autowired 125 | private DataSource dataSource; 126 | 127 | @Bean 128 | public PersistentTokenRepository persistentTokenRepository(){ 129 | JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); 130 | // 设置数据源 默认使用的Apache的连接池 131 | jdbcTokenRepository.setDataSource(dataSource); 132 | // 设置初始化存储Token的表 方便调试 由于源码没有对数据库中是否有表结构做出判断,正常使用的时候不建议开启,不然第二次启动会报错! 133 | // jdbcTokenRepository.setCreateTableOnStartup(true); 134 | return jdbcTokenRepository; 135 | } 136 | 137 | @Autowired 138 | private CustomInvocationSecurityMetadataSourceService mySecurityMetadataSource; 139 | 140 | @Autowired 141 | private CustomAccessDecisionManager myAccessDecisionManager; 142 | 143 | @Autowired 144 | private AuthenticationManager authenticationManager; 145 | 146 | @Bean 147 | public FilterSecurityInterceptor filterSecurityInterceptor(){ 148 | FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor(); 149 | filterSecurityInterceptor.setSecurityMetadataSource(mySecurityMetadataSource); 150 | filterSecurityInterceptor.setAccessDecisionManager(myAccessDecisionManager); 151 | filterSecurityInterceptor.setAuthenticationManager(authenticationManager); 152 | return filterSecurityInterceptor; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/controller/BasicController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class BasicController { 8 | 9 | @RequestMapping(value = {"/","/home"}) 10 | public String home(){ 11 | return "home"; 12 | } 13 | 14 | @RequestMapping(value = {"/admin"}) 15 | public String admin(){ 16 | return "admin"; 17 | } 18 | 19 | @RequestMapping(value = {"/user"}) 20 | public String user(){ 21 | return "user"; 22 | } 23 | 24 | @RequestMapping(value = {"/vip"}) 25 | public String vip(){ 26 | return "vip"; 27 | } 28 | @RequestMapping(value = {"/login"}) 29 | public String login(){ 30 | return "login"; 31 | } 32 | 33 | @RequestMapping(value = {"/super"}) 34 | public String superm(){ 35 | return "super"; 36 | } 37 | @RequestMapping(value = {"/deny"}) 38 | public String deny(){ 39 | return "deny"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/model/SysResource.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import javax.persistence.*; 4 | 5 | /** 6 | * 资源类:本实例指的是访问路径 7 | */ 8 | @Entity 9 | public class SysResource { 10 | @Id 11 | @GeneratedValue(strategy= GenerationType.IDENTITY) 12 | @Column (name="id",length=10) 13 | private Integer id; 14 | 15 | @Column(name="resourceName",length=1000) 16 | private String resourceName; 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public void setId(int id) { 23 | this.id = id; 24 | } 25 | 26 | public void setId(Integer id) { 27 | this.id = id; 28 | } 29 | 30 | public String getResourceName() { 31 | return resourceName; 32 | } 33 | 34 | public void setResourceName(String resourceName) { 35 | this.resourceName = resourceName; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "SysResource{" + 41 | "id=" + id + 42 | ", resourceName='" + resourceName + '\'' + 43 | '}'; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/model/SysResourceRole.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import javax.persistence.*; 4 | import java.util.Date; 5 | 6 | /** 7 | * 资源与角色中间表 8 | */ 9 | @Entity 10 | public class SysResourceRole { 11 | @Id 12 | @GeneratedValue(strategy= GenerationType.IDENTITY) 13 | @Column (name="id",length=10) 14 | private int id; 15 | 16 | @Column(name="roleId",length=50) 17 | private String roleId; 18 | 19 | @Column(name="resourceId",length=50) 20 | private String resourceId; 21 | 22 | 23 | public int getId() { 24 | return id; 25 | } 26 | 27 | public void setId(int id) { 28 | this.id = id; 29 | } 30 | 31 | public String getRoleId() { 32 | return roleId; 33 | } 34 | 35 | public void setRoleId(String roleId) { 36 | this.roleId = roleId; 37 | } 38 | 39 | public String getResourceId() { 40 | return resourceId; 41 | } 42 | 43 | public void setResourceId(String resourceId) { 44 | this.resourceId = resourceId; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "SysResourceRole{" + 50 | "id=" + id + 51 | ", roleId='" + roleId + '\'' + 52 | ", resourceId='" + resourceId + '\'' + 53 | '}'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/model/SysRole.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import javax.persistence.*; 4 | 5 | 6 | /** 7 | * 角色类 8 | */ 9 | @Entity 10 | public class SysRole { 11 | @Id 12 | @GeneratedValue(strategy= GenerationType.IDENTITY) 13 | @Column (name="id",length=10) 14 | private Integer id; 15 | 16 | @Column(name="roleName",length=100) 17 | private String roleName; 18 | 19 | public Integer getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Integer id) { 24 | this.id = id; 25 | } 26 | 27 | public String getRoleName() { 28 | return roleName; 29 | } 30 | 31 | public void setRoleName(String roleName) { 32 | this.roleName = roleName; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "SysRole{" + 38 | "id=" + id + 39 | ", roleName='" + roleName + '\'' + 40 | '}'; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/model/SysUser.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | import javax.persistence.*; 8 | import java.util.*; 9 | 10 | /** 11 | * 用户类 12 | */ 13 | @Entity 14 | public class SysUser implements UserDetails { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | @Column(name = "id", unique = true, nullable = false) 19 | private Integer id; 20 | @Column(name = "username", length = 120) 21 | private String username; 22 | 23 | @Column(name = "password", length = 120) 24 | private String password; 25 | 26 | @ManyToMany(cascade = CascadeType.REFRESH,fetch = FetchType.EAGER) 27 | private List roles; 28 | 29 | public SysUser() { 30 | } 31 | 32 | @Override 33 | public boolean isAccountNonExpired() { 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean isAccountNonLocked() { 39 | return true; 40 | } 41 | 42 | @Override 43 | public boolean isCredentialsNonExpired() { 44 | return true; 45 | } 46 | 47 | @Override 48 | public boolean isEnabled() { 49 | return true; 50 | } 51 | 52 | 53 | @Override 54 | public Collection getAuthorities() { 55 | Collection authorities = new ArrayList<>(); 56 | List userRoles = this.getRoles(); 57 | if(userRoles != null){ 58 | for (SysRole role : userRoles) { 59 | SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleName()); 60 | authorities.add(authority); 61 | } 62 | } 63 | return authorities; 64 | } 65 | 66 | public Integer getId() { 67 | return this.id; 68 | } 69 | 70 | public void setId(Integer id) { 71 | this.id = id; 72 | } 73 | 74 | public void setUsername(String username) { 75 | this.username = username; 76 | } 77 | 78 | public String getUsername() { 79 | return username; 80 | } 81 | 82 | public String getPassword() { 83 | return this.password; 84 | } 85 | 86 | public void setPassword(String password) { 87 | this.password = password; 88 | } 89 | 90 | public List getRoles() { 91 | return roles; 92 | } 93 | 94 | public void setRoles(List roles) { 95 | this.roles = roles; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return "SysUser{" + 101 | "id=" + id + 102 | ", username='" + username + '\'' + 103 | ", password='" + password + '\'' + 104 | '}'; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/repository/SysResourceRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.SysResource; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | public interface SysResourceRepository extends JpaRepository { 10 | 11 | /** 12 | * 通过角色名称获取资源列表 13 | * @param rolename 14 | * @return 15 | * 16 | */ 17 | @Query(value = "SELECT * FROM sys_resource WHERE id IN ( SELECT resource_id FROM sys_resource_role WHERE role_id = ( SELECT id FROM sys_role WHERE role_name = ?1))",nativeQuery = true) 18 | List findByRoleName(String rolename); 19 | } 20 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/repository/SysRoleRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.SysRole; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface SysRoleRepository extends JpaRepository{ 9 | 10 | @Override 11 | List findAll(); 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/entity/repository/SysUserRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.SysUser; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface SysUserRepository extends JpaRepository { 7 | 8 | SysUser findByUsername(String username); 9 | } 10 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/service/CustomAccessDecisionManager.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import org.springframework.security.access.AccessDecisionManager; 4 | import org.springframework.security.access.AccessDeniedException; 5 | import org.springframework.security.access.ConfigAttribute; 6 | import org.springframework.security.access.SecurityConfig; 7 | import org.springframework.security.authentication.InsufficientAuthenticationException; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.Collection; 13 | import java.util.Iterator; 14 | 15 | /** 16 | *AccessdecisionManager在Spring security中是很重要的。 17 | * 18 | *在验证部分简略提过了,所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。 19 | *这就是赋予给主体的权限。 GrantedAuthority对象通过AuthenticationManager 20 | *保存到 Authentication对象里,然后从AccessDecisionManager读出来,进行授权判断。 21 | * 22 | *Spring Security提供了一些拦截器,来控制对安全对象的访问权限,例如方法调用或web请求。 23 | *一个是否允许执行调用的预调用决定,是由AccessDecisionManager实现的。 24 | *这个 AccessDecisionManager 被AbstractSecurityInterceptor调用, 25 | *它用来作最终访问控制的决定。 这个AccessDecisionManager接口包含三个方法: 26 | * 27 | void decide(Authentication authentication, Object secureObject,List config) throws AccessDeniedException; 28 | boolean supports(ConfigAttribute attribute); 29 | boolean supports(Class clazz); 30 | 31 | 从第一个方法可以看出来,AccessDecisionManager使用方法参数传递所有信息,这好像在认证评估时进行决定。 32 | 特别是,在真实的安全方法期望调用的时候,传递安全Object启用那些参数。 33 | 比如,让我们假设安全对象是一个MethodInvocation。 34 | 很容易为任何Customer参数查询MethodInvocation, 35 | 然后在AccessDecisionManager里实现一些有序的安全逻辑,来确认主体是否允许在那个客户上操作。 36 | 如果访问被拒绝,实现将抛出一个AccessDeniedException异常。 37 | 38 | 这个 supports(ConfigAttribute) 方法在启动的时候被 39 | AbstractSecurityInterceptor调用,来决定AccessDecisionManager 40 | 是否可以执行传递ConfigAttribute。 41 | supports(Class)方法被安全拦截器实现调用, 42 | 包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。 43 | */ 44 | @Service 45 | public class CustomAccessDecisionManager implements AccessDecisionManager { 46 | 47 | //这里验证完了之后,抛出的异常,想要捕捉,需要ExceptionTranslationFilter来进行处理 48 | public void decide( Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{ 49 | if( configAttributes == null ) { 50 | return ; 51 | } 52 | Iterator iterator = configAttributes.iterator(); 53 | while( iterator.hasNext()){ 54 | ConfigAttribute configAttribute = iterator.next(); 55 | String needRole = ((SecurityConfig)configAttribute).getAttribute(); 56 | //grantedAuthority 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。 57 | for( GrantedAuthority grantedAuthority: authentication.getAuthorities()){ 58 | if(needRole.trim().equals(grantedAuthority.getAuthority().trim())){ 59 | return; 60 | } 61 | } 62 | } 63 | /** 64 | * 如果一个AccessDeniedException被抛出并且用户已经被认证,那么这意味着一个操作已经尝试了它们不具有足够的权限。 65 | * 在这种情况下,ExceptionTranslationFilter将调用第二策略,AccessDeniedHandler。 66 | * 默认情况下,AccessDeniedHandlerImpl被使用,这只是发送一个403(禁止)响应于客户端。 67 | * 此外,还可以配置明确的实例,并设置一个错误页面的URL,它会请求转发 . 68 | * 这可以是一个简单的“拒绝访问”页上,如一个JSP,或者它可以是更复杂的处理程序,如一个MVC的控制器。 69 | * 当然,你可以自己实现接口,并使用自己的实现。 70 | */ 71 | throw new AccessDeniedException("权限不足"); 72 | } 73 | 74 | public boolean supports( ConfigAttribute attribute ){ 75 | return true;//都要设为true 76 | 77 | } 78 | 79 | public boolean supports(Class clazz){ 80 | return true;//都要设为true 81 | } 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/service/CustomInvocationSecurityMetadataSourceService.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import cn.geekview.entity.model.SysResource; 4 | import cn.geekview.entity.model.SysRole; 5 | import cn.geekview.entity.repository.SysResourceRepository; 6 | import cn.geekview.entity.repository.SysRoleRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.access.ConfigAttribute; 9 | import org.springframework.security.access.SecurityConfig; 10 | import org.springframework.security.web.FilterInvocation; 11 | import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 12 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 13 | import org.springframework.security.web.util.matcher.RequestMatcher; 14 | import org.springframework.stereotype.Service; 15 | 16 | import javax.annotation.PostConstruct; 17 | import java.util.*; 18 | 19 | 20 | /** 21 | * 最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。 22 | */ 23 | @Service 24 | public class CustomInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { 25 | 26 | @Autowired 27 | private SysRoleRepository roleRepository; 28 | 29 | @Autowired 30 | private SysResourceRepository resourceRepository; 31 | 32 | private static Map> resourceMap = null; 33 | 34 | /*public CustomInvocationSecurityMetadataSourceService(SResourceService sres,SRoleService sR) { 35 | this.sResourceService = sres; 36 | this.sRoleService = sR; 37 | loadResourceDefine(); 38 | }*/ 39 | 40 | /** 41 | * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。 42 | * PostConstruct在构造函数之后执行,init()方法之前执行。 43 | * 此处一定要加上@PostConstruct注解 44 | * 45 | * 注意: 形成以URL为Key,权限列表为Value的Map时,注意Key和Value的对应性, 46 | * 避免Value的不正确对应形成重复,这样会导致没有权限的也能访问到不该访问的资源 47 | */ 48 | @PostConstruct 49 | private void loadResourceDefine() { 50 | // 在Web服务器启动时,提取系统中的所有权限。 51 | List roleList =roleRepository.findAll(); 52 | 53 | List roleNameList = new ArrayList(); 54 | if(roleList!=null && roleList.size()>0) { 55 | for (SysRole role : roleList) { 56 | roleNameList.add(role.getRoleName()); 57 | } 58 | } 59 | /* 60 | * 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 61 | * 一个资源可以由多个权限来访问。 62 | * 63 | */ 64 | resourceMap = new HashMap>(); 65 | for (String roleName : roleNameList) { 66 | // 将角色名封装一个安全配置???这样理解有点不准确, 说白了就是一个角色名,只是为了和框架整合换一种说法 67 | ConfigAttribute configAttribute = new SecurityConfig(roleName); 68 | List urlList = new ArrayList(); 69 | List resourceList = resourceRepository.findByRoleName(roleName); 70 | if(resourceList!=null && resourceList.size()>0) {//如果不加判断,这里如果 resourceRepository.findByRoleName(roleName);为null.则会报错 71 | for(SysResource resource :resourceList){ 72 | urlList.add(resource.getResourceName()); 73 | } 74 | } 75 | for (String res : urlList) { 76 | String url = res; 77 | /* 78 | * 判断资源文件和权限的对应关系,如果已经存在相关的资源url, 79 | * 则要通过该url为key提取出权限集合,将权限增加到权限集合中。 80 | * 81 | */ 82 | if (resourceMap.containsKey(url)) { 83 | Collection value = resourceMap.get(url); 84 | value.add(configAttribute); 85 | resourceMap.put(url, value); 86 | } else { 87 | Collection atts = new ArrayList(); 88 | atts.add(configAttribute); 89 | resourceMap.put(url, atts);// 一个URL对应多种角色 90 | } 91 | } 92 | } 93 | 94 | } 95 | 96 | /** 97 | * 这里这样写有问题,获取的是一个空的 98 | * @return 99 | */ 100 | @Override 101 | public Collection getAllConfigAttributes() { 102 | return new ArrayList(); 103 | } 104 | 105 | /** 106 | * 根据URL,找到相关的权限配置。 107 | * 要有URL.indexof("?")这样的判断,主要是对URL你问号之前的匹配,避免对用户请求带参数的URL与数据库里面的URL不匹配,造成访问拒绝 108 | */ 109 | @Override 110 | public Collection getAttributes(Object object) throws IllegalArgumentException { 111 | // object 是一个URL,被用户请求的url。 112 | FilterInvocation filterInvocation = (FilterInvocation) object; 113 | if (resourceMap == null) { 114 | loadResourceDefine(); 115 | } 116 | Iterator iterator = resourceMap.keySet().iterator(); 117 | while (iterator.hasNext()) { 118 | String resURL = iterator.next(); 119 | // 优化请求路径后面带参数的部分 120 | RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL); 121 | if(requestMatcher.matches(filterInvocation.getHttpRequest())) { 122 | return resourceMap.get(resURL); 123 | } 124 | } 125 | return null; 126 | } 127 | @Override 128 | public boolean supports(Class arg0) { 129 | return true; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /springsecurity2/src/main/java/cn/geekview/service/CustomUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import cn.geekview.entity.model.SysUser; 4 | import cn.geekview.entity.repository.SysUserRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class CustomUserDetailsService implements UserDetailsService { 13 | 14 | @Autowired 15 | private SysUserRepository userReporitory; 16 | 17 | @Override 18 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 19 | //SysUser对应数据库中的用户表,是最终存储用户和密码的表,可自定义 20 | //本例使用SysUser中的name作为用户名: 21 | SysUser user = userReporitory.findByUsername(username); 22 | if (user == null) { 23 | throw new UsernameNotFoundException("UserName " + username + " not found"); 24 | } 25 | return user; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScnuWang/SpringBoot/dab0a4c95389a69f3dbaadb09d28741e4d706c66/springsecurity2/src/main/resources/application.properties -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ADMIN 5 | 6 | 7 | 8 | 这里是ADMIN页面!!!!! 9 | 10 | 访问: 11 | 首页 12 | 普通用户页面 13 | VIP用户页面 14 | 管理员页面 15 | 超级管理员页面 16 | 17 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/deny.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 5 | 6 | 7 | 8 | 9 | 请求异常信息: 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HOME 5 | 6 | 7 | 8 | 这里是首页!!! 9 | 10 | 11 |
12 | 13 |
14 | 15 | 访问: 16 | 首页 17 | 普通用户页面 18 | VIP用户页面 19 | 管理员页面 20 | 超级管理员页面 21 | 22 | 23 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Spring Security Example 6 | 7 | 8 |
9 | 用户名和密码不正确 10 |
11 |
12 | 注销成功 13 |
14 |
15 |
16 |
17 |
18 |

记住我

19 |
20 | 21 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/super.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SUPER 5 | 6 | 7 | 8 | 9 | 这里是SUPER用户的页面!!! 10 | 11 | 访问: 12 | 首页 13 | 普通用户页面 14 | VIP用户页面 15 | 管理员页面 16 | 超级管理员页面 17 | 18 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | USER 5 | 6 | 7 | 8 | 这里是普通注册用户的页面!!! 9 | 访问: 10 | 首页 11 | 普通用户页面 12 | VIP用户页面 13 | 管理员页面 14 | 超级管理员页面 15 | 16 | 17 | -------------------------------------------------------------------------------- /springsecurity2/src/main/resources/templates/vip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VIP 5 | 6 | 7 | 8 | 9 | 这里是VIP页面!!! 10 | 11 | 访问: 12 | 首页 13 | 普通用户页面 14 | VIP用户页面 15 | 管理员页面 16 | 超级管理员页面 17 | 18 | -------------------------------------------------------------------------------- /springsecurity2/src/test/java/cn/geekview/BasicTest.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.junit.Test; 4 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 5 | 6 | public class BasicTest { 7 | 8 | @Test 9 | public void test(){ 10 | BCryptPasswordEncoder bc=new BCryptPasswordEncoder(4);//将密码加密 11 | System.out.println("密码:"+ bc.encode("123456")); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /springsecurity2/src/test/java/cn/geekview/Springsecurity2ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import cn.geekview.entity.repository.SysResourceRepository; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | 10 | @RunWith(SpringRunner.class) 11 | @SpringBootTest 12 | public class Springsecurity2ApplicationTests { 13 | 14 | @Autowired 15 | private SysResourceRepository resourceRepository; 16 | 17 | @Test 18 | public void contextLoads() { 19 | System.out.println(resourceRepository.findByRoleName("VIP")); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /springsecurity3/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /springsecurity3/README.md: -------------------------------------------------------------------------------- 1 | ### 注册 2 | 知识点复习: 3 | 4 | JPA相关 5 | 6 | 流程: 7 | 8 | 首页--》注册--》填写基本信息--》提交表单--》 9 | 持久化基本信息到数据库、发送验证邮件--》 10 | 邮箱验证--》登录页登录 11 | 12 | 需求: 13 | 14 | 1、所有必填字段是否都已填写(无空白字段或 null 字段) 15 | 2、电子邮件地址是否有效(格式是否正确) 16 | 3、密码确认字段与密码字段是否匹配(前端匹配,后端不需要) 17 | 4、帐户是否已经注册 18 | 5、为每个用户生成校验Token,并持久化 19 | 6、给注册邮箱发送带有校验Token的邮件 20 | 7、密码强度校验提示 21 | 8、注册登录都使用HMAC 算法校验 -------------------------------------------------------------------------------- /springsecurity3/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /springsecurity3/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /springsecurity3/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cn.geekview 7 | springsecurity3 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | springsecurity3 12 | Module for Spring Boot With Spring Security For Registeration Process 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-security 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-thymeleaf 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-devtools 48 | runtime 49 | 50 | 51 | mysql 52 | mysql-connector-java 53 | runtime 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | org.springframework.security 62 | spring-security-test 63 | test 64 | 65 | 66 | org.projectlombok 67 | lombok 68 | compile 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-mail 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-maven-plugin 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/Springsecurity3Application.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Springsecurity3Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Springsecurity3Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/annotation/EmailValidator.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.annotation; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * 邮箱校验:由于 org.hibernate.validator.constraints.Email的注解校验不能识别:abc@abc这种邮箱地址 10 | */ 11 | public class EmailValidator implements ConstraintValidator { 12 | 13 | private Pattern pattern; 14 | private Matcher matcher; 15 | private static final String EMAIL_PATTERN = "[A-Za-z0-9._%-+]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; 16 | @Override 17 | public void initialize(ValidEmail constraintAnnotation) { 18 | } 19 | @Override 20 | public boolean isValid(String email, ConstraintValidatorContext context){ 21 | return (validateEmail(email)); 22 | } 23 | private boolean validateEmail(String email) { 24 | pattern = Pattern.compile(EMAIL_PATTERN); 25 | matcher = pattern.matcher(email); 26 | return matcher.matches(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/annotation/ValidEmail.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.annotation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 10 | import static java.lang.annotation.ElementType.FIELD; 11 | import static java.lang.annotation.ElementType.TYPE; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | @Target({TYPE, FIELD, ANNOTATION_TYPE}) 15 | @Retention(RUNTIME) 16 | @Constraint(validatedBy = EmailValidator.class) 17 | @Documented 18 | public @interface ValidEmail { 19 | String message() default "Invalid email"; 20 | Class[] groups() default {}; 21 | Class[] payload() default {}; 22 | } 23 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config; 2 | 3 | import cn.geekview.service.impl.UserServiceImpl; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.security.crypto.password.PasswordEncoder; 13 | 14 | @Configuration 15 | @EnableWebSecurity 16 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 17 | 18 | @Autowired 19 | private UserServiceImpl userService; 20 | 21 | @Override 22 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 23 | //注册自己的UserService 24 | auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); 25 | } 26 | 27 | @Override 28 | protected void configure(HttpSecurity http) throws Exception { 29 | //所有请求都允许访问 30 | http.authorizeRequests().anyRequest().permitAll().and().csrf().disable(); 31 | } 32 | 33 | @Bean 34 | public PasswordEncoder passwordEncoder(){ 35 | return new BCryptPasswordEncoder(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | 6 | @Controller 7 | public class IndexController { 8 | 9 | @GetMapping("/login") 10 | public String login(){ 11 | return "login"; 12 | } 13 | 14 | @GetMapping("/regist") 15 | public String regist(){ 16 | return "regist"; 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/controller/RegistrationController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.entity.model.VerificationToken; 5 | import cn.geekview.service.IUserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.ui.Model; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.context.request.WebRequest; 12 | 13 | import java.util.Calendar; 14 | 15 | @Controller 16 | public class RegistrationController { 17 | @Autowired 18 | private IUserService service; 19 | 20 | @GetMapping(value = "/regitrationConfirm") 21 | public String confirmRegistration (WebRequest request, Model model, @RequestParam("token") String token) { 22 | 23 | VerificationToken verificationToken = service.getVerificationToken(token); 24 | //验证令牌不存在 25 | if (verificationToken == null) { 26 | model.addAttribute("message", "验证令牌不存在!!!"); 27 | return "redirect:/emailError.html"; 28 | } 29 | User user = verificationToken.getUser(); 30 | Calendar cal = Calendar.getInstance(); 31 | if ((verificationToken.getExpiryDate().getTime() - cal.getTime().getTime()) <= 0) { 32 | model.addAttribute("message", "验证令牌已过期!!!"); 33 | return "redirect:/emailError.html"; 34 | } 35 | user.setEnabled(true); 36 | service.saveRegisteredUser(user); 37 | return "redirect:/login";//这样直接重定向的路径需要在Controller的路径上存在的路径不然会报错!!! 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.event.OnRegistrationCompleteEvent; 5 | import cn.geekview.service.IUserService; 6 | import cn.geekview.service.impl.UserServiceImpl; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.ApplicationEventPublisher; 10 | import org.springframework.http.HttpRequest; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.validation.BindingResult; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.context.request.WebRequest; 17 | import org.springframework.web.servlet.ModelAndView; 18 | 19 | import javax.validation.Valid; 20 | 21 | @Controller 22 | @Slf4j 23 | public class UserController { 24 | 25 | @Autowired 26 | private ApplicationEventPublisher eventPublisher; 27 | 28 | @Autowired 29 | private IUserService userService; 30 | 31 | @RequestMapping(value = {"/","/home"}) 32 | public String home(){ 33 | return "home"; 34 | } 35 | 36 | @RequestMapping(value = {"/login"}) 37 | public String login(){ 38 | return "login"; 39 | } 40 | 41 | @RequestMapping(value = {"/register"}) 42 | public String register(Model model){ 43 | return "register"; 44 | } 45 | 46 | /** 47 | * @Valid的参数后必须紧挨着一个BindingResult 参数,否则spring会在校验不通过时直接抛出异常 48 | * @param user 49 | * @param result 50 | * @return 51 | */ 52 | @RequestMapping(value = "/registration", method = RequestMethod.POST) 53 | public ModelAndView registerUserAccount (@Valid User user, BindingResult result, WebRequest request) { 54 | 55 | if (result.hasErrors()) { 56 | return new ModelAndView("register", "user", user); 57 | } 58 | 59 | User registered = createUserAccount(user); 60 | if (registered == null) { 61 | result.reject("emailError","该邮箱已经注册!!!"); 62 | return new ModelAndView("register", "message", "该邮箱已经注册!!!"); 63 | } 64 | // 发布注册完成事件 65 | try { 66 | String appUrl = request.getContextPath(); 67 | eventPublisher.publishEvent(new OnRegistrationCompleteEvent (registered, appUrl)); 68 | } catch (Exception me) { 69 | return new ModelAndView("emailError", "message", "邮件发送异常,请检查您的邮箱地址是否正确"); 70 | } 71 | return new ModelAndView("home", "user", user); 72 | } 73 | 74 | private User createUserAccount(User user) { 75 | User registered = null; 76 | try { 77 | registered = userService.registerNewUserAccount(user); 78 | } catch (Exception e) { 79 | return null; 80 | } 81 | return registered; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/model/Resource.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import javax.persistence.*; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | @Entity 8 | @Table(name = "t_resource") 9 | public class Resource { 10 | 11 | @Id 12 | @GeneratedValue 13 | private Integer id; 14 | 15 | private String name;//资源名称 16 | 17 | @ManyToMany(mappedBy = "resources",cascade = CascadeType.ALL,fetch = FetchType.LAZY) 18 | private Set roles = new HashSet<>();//一个资源可以被多个角色调用 19 | } 20 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/model/Role.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | @Data 11 | @Entity 12 | @Table(name = "t_role") 13 | public class Role { 14 | @Id 15 | @GeneratedValue 16 | private Integer id; 17 | 18 | private String name;//角色名称 19 | 20 | /** 21 | * mappedBy:声明于关系的被维护方,声明的值为关系的维护方的关系对象属性名。 22 | * 被维护方不会主动去维护关联关系,真正的关系维护,掌握在维护方的手中。 23 | * cascade:级联关系,给当前设置的实体操作另一个实体的权限,不要随便给all权限操作,应该根据业务需求选择所需的级联关系. 24 | * 更多解释:https://www.jianshu.com/p/e8caafce5445 25 | */ 26 | @ManyToMany(mappedBy = "roles",cascade = {CascadeType.MERGE,CascadeType.DETACH},fetch = FetchType.LAZY) 27 | private Set users = new HashSet<>();//一个角色可以有多个用户 28 | 29 | @ManyToMany(cascade = CascadeType.DETACH,fetch = FetchType.LAZY) 30 | private Set resources = new HashSet<>();//一个角色可以调用多个资源 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/model/User.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import cn.geekview.annotation.ValidEmail; 4 | import lombok.Data; 5 | import org.hibernate.validator.constraints.NotEmpty; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | 10 | import javax.persistence.*; 11 | import javax.validation.constraints.NotNull; 12 | import java.util.Collection; 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | 17 | @Data 18 | @Entity 19 | @Table(name = "t_user") 20 | public class User implements UserDetails{ 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.AUTO) 24 | private Integer id;//主键 25 | 26 | @NotNull 27 | @NotEmpty 28 | private String username;//用户名 29 | 30 | @NotNull 31 | @NotEmpty 32 | private String password;//密码 33 | 34 | @NotNull 35 | @NotEmpty 36 | @ValidEmail 37 | private String email;//邮箱 38 | 39 | private boolean enabled;//是否激活 40 | 41 | /** 42 | * CascadeType.DETACH 43 | Cascade detach operation,级联脱管/游离操作。 44 | 如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联。 45 | */ 46 | @ManyToMany(cascade = CascadeType.DETACH,fetch = FetchType.LAZY) 47 | private Set roles = new HashSet<>(); 48 | 49 | public User(){ 50 | super(); 51 | this.enabled=false; 52 | } 53 | 54 | // 获取用户有哪些角色(对应框架就是有哪些权限) 55 | @Override 56 | public Collection getAuthorities() { 57 | Collection grantedAuthorities = new HashSet<>(); 58 | Set roles = this.roles; 59 | if (roles != null) { 60 | for (Role role : roles) { 61 | SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName()); 62 | grantedAuthorities.add(grantedAuthority); 63 | } 64 | } 65 | return grantedAuthorities; 66 | } 67 | 68 | @Override 69 | public boolean isAccountNonExpired() { 70 | return true; 71 | } 72 | 73 | @Override 74 | public boolean isAccountNonLocked() { 75 | return true; 76 | } 77 | 78 | @Override 79 | public boolean isCredentialsNonExpired() { 80 | return true; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/model/VerificationToken.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.model; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.sql.Timestamp; 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | 10 | @Data 11 | @Entity 12 | public class VerificationToken { 13 | private static final int EXPIRATION = 60 * 24; 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.AUTO) 17 | private Long id; 18 | 19 | private String token; 20 | 21 | @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER) 22 | @JoinColumn(nullable = false, name = "user_id") 23 | private User user; 24 | 25 | private Date expiryDate; 26 | 27 | private Date calculateExpiryDate(int expiryTimeInMinutes) { 28 | Calendar cal = Calendar.getInstance(); 29 | cal.setTime(new Timestamp(cal.getTime().getTime())); 30 | cal.add(Calendar.MINUTE, expiryTimeInMinutes); 31 | return new Date(cal.getTime().getTime()); 32 | } 33 | public VerificationToken(){ 34 | 35 | } 36 | public VerificationToken(String token , User user){ 37 | this.user = user; 38 | this.token=token; 39 | this.expiryDate=this.calculateExpiryDate(this.EXPIRATION); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserRepository extends JpaRepository { 7 | 8 | User findByEmail(String email); 9 | 10 | User findByUsername(String username); 11 | } 12 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/entity/repository/VerificationTokenRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.entity.repository; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.entity.model.VerificationToken; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface VerificationTokenRepository extends JpaRepository { 8 | 9 | VerificationToken findByToken(String token); 10 | 11 | VerificationToken findByUser(User user); 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/event/Listener/RegistrationListener.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.event.Listener; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.event.OnRegistrationCompleteEvent; 5 | import cn.geekview.service.IUserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.ApplicationListener; 8 | import org.springframework.context.MessageSource; 9 | import org.springframework.core.env.Environment; 10 | import org.springframework.mail.SimpleMailMessage; 11 | import org.springframework.mail.javamail.JavaMailSender; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.UUID; 15 | 16 | @Component 17 | public class RegistrationListener implements ApplicationListener { 18 | @Autowired 19 | private IUserService service; 20 | 21 | @Autowired 22 | private JavaMailSender mailSender; 23 | 24 | @Autowired 25 | private Environment env; 26 | 27 | @Override 28 | public void onApplicationEvent(final OnRegistrationCompleteEvent event) { 29 | this.confirmRegistration(event); 30 | } 31 | 32 | private void confirmRegistration(final OnRegistrationCompleteEvent event) { 33 | final User user = event.getUser(); 34 | final String token = UUID.randomUUID().toString(); 35 | service.createVerificationTokenForUser(user, token); 36 | 37 | final SimpleMailMessage email = constructEmailMessage(event, user, token); 38 | mailSender.send(email); 39 | } 40 | 41 | 42 | private final SimpleMailMessage constructEmailMessage(final OnRegistrationCompleteEvent event, final User user, final String token) { 43 | final String recipientAddress = user.getEmail(); 44 | final String subject = "注册邮箱激活"; 45 | final String confirmationUrl = event.getAppUrl() + "/regitrationConfirm?token=" + token; 46 | final SimpleMailMessage email = new SimpleMailMessage(); 47 | email.setTo(recipientAddress); 48 | email.setSubject(subject); 49 | email.setText("http://localhost:80"+confirmationUrl); 50 | email.setFrom(env.getProperty("mail.from.addr")); 51 | return email; 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/event/OnRegistrationCompleteEvent.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.event; 2 | 3 | import cn.geekview.entity.model.User; 4 | import org.springframework.context.ApplicationEvent; 5 | 6 | /** 7 | * ApplicationEvent ????? 8 | */ 9 | public class OnRegistrationCompleteEvent extends ApplicationEvent { 10 | 11 | private final String appUrl; 12 | private final User user; 13 | 14 | public OnRegistrationCompleteEvent(final User user, final String appUrl) { 15 | super(user); 16 | this.user = user; 17 | this.appUrl = appUrl; 18 | } 19 | 20 | // 21 | 22 | public String getAppUrl() { 23 | return appUrl; 24 | } 25 | 26 | 27 | public User getUser() { 28 | return user; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.entity.model.VerificationToken; 5 | import org.springframework.security.core.userdetails.UserDetailsService; 6 | 7 | public interface IUserService extends UserDetailsService { 8 | 9 | User findByEmail(String email); 10 | 11 | void createVerificationTokenForUser(User user,String token); 12 | 13 | VerificationToken getVerificationToken(String VerificationToken); 14 | 15 | User getUser(String verificationToken); 16 | 17 | void saveRegisteredUser(User user); 18 | 19 | User registerNewUserAccount(User user) throws Exception; 20 | } 21 | -------------------------------------------------------------------------------- /springsecurity3/src/main/java/cn/geekview/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service.impl; 2 | 3 | import cn.geekview.entity.model.User; 4 | import cn.geekview.entity.model.VerificationToken; 5 | import cn.geekview.entity.repository.UserRepository; 6 | import cn.geekview.entity.repository.VerificationTokenRepository; 7 | import cn.geekview.service.IUserService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.stereotype.Service; 13 | 14 | import javax.transaction.Transactional; 15 | 16 | @Service 17 | public class UserServiceImpl implements IUserService { 18 | 19 | @Autowired 20 | private UserRepository userRepository; 21 | 22 | @Autowired 23 | private VerificationTokenRepository tokenRepository; 24 | 25 | @Autowired 26 | private PasswordEncoder passwordEncoder; 27 | 28 | @Transactional 29 | public User registerNewUserAccount(User user) throws Exception { 30 | if (isEmailExist(user.getEmail())) { 31 | throw new Exception("邮箱已经注册!!!"); 32 | }else { 33 | user.setPassword(passwordEncoder.encode(user.getPassword())); 34 | return userRepository.save(user); 35 | } 36 | 37 | } 38 | 39 | private boolean isEmailExist(String email) { 40 | User user = findByEmail(email); 41 | if (user != null) { 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | @Override 48 | public User findByEmail(String email) { 49 | return userRepository.findByEmail(email); 50 | } 51 | 52 | @Override 53 | public void createVerificationTokenForUser(User user, String token) { 54 | VerificationToken myToken = new VerificationToken(token, user); 55 | tokenRepository.save(myToken); 56 | } 57 | 58 | @Override 59 | public VerificationToken getVerificationToken(String VerificationToken) { 60 | return tokenRepository.findByToken(VerificationToken); 61 | } 62 | 63 | @Override 64 | public User getUser(String verificationToken) { 65 | return tokenRepository.findByToken(verificationToken).getUser(); 66 | } 67 | 68 | @Override 69 | public void saveRegisteredUser(User user) { 70 | userRepository.save(user); 71 | } 72 | 73 | @Override 74 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 75 | User user = userRepository.findByUsername(username); 76 | if (user == null) { 77 | throw new UsernameNotFoundException("用户名:"+username+" 不存在!"); 78 | } 79 | return user; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /springsecurity3/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScnuWang/SpringBoot/dab0a4c95389a69f3dbaadb09d28741e4d706c66/springsecurity3/src/main/resources/application.properties -------------------------------------------------------------------------------- /springsecurity3/src/main/resources/templates/emailError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 邮箱验证 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /springsecurity3/src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 首页 5 | 6 | 7 | 8 | 这里是首页!!! 9 | 注册 10 | 登录 11 | 12 | -------------------------------------------------------------------------------- /springsecurity3/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 登录 5 | 6 | 7 | 8 | 登录页面 9 | 10 | -------------------------------------------------------------------------------- /springsecurity3/src/main/resources/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 注册 5 | 6 | 7 | 8 |

form

9 |
10 | 11 | 用户名: 12 | 密 码: 13 | 邮 箱: 14 | 15 | 16 |
17 | 18 | 19 | login 20 | 21 | -------------------------------------------------------------------------------- /springsecurity3/src/test/java/cn/geekview/Springsecurity3ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class Springsecurity3ApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springsecurity4/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /springsecurity4/README.md: -------------------------------------------------------------------------------- 1 | ### 本模块集成登录注册整个流程以及对资源的访问控制 2 | 3 | 初始需求: 4 | 5 | 1、邮箱注册登录 6 | 2、邮箱激活验证 7 | 3、资源访问控制 8 | 4、密码使用BCrypt加密 9 | 10 | 需求升级: 11 | 12 | 1、OAuth授权登录 [x] 13 | 2、密码强度校验 [x] 14 | 3、HMAC校验 [x] 15 | 16 | 17 | 问题解答: 18 | 19 | 1、JPA多对多映射关系出现死循环导致内存泄露报错 20 | 答:找了大半天的问题,按照@ManyToMany源码示例配置仍然错误,最后去掉lombok的@Data注解解决问题,使用原始setter,getter。暂不知道原因??? 21 | 22 | 2、AuthenticationException异常没有捕获到,即使用authenticationEntryPoint没有效果 23 | 答:用户注册之后没有激活,导致产生DisabledException,但是却没有自定义被注册的authenticationEntryPoint拦截,估计是SpringSecurity框架将这种情况当做是没有登录成功,如果不自定义登录失败的Handler默认使用SimpleUrlAuthenticationFailureHandler 24 | 跳转到login?error这种情况要获取异常信息可使用:获取异常信息 25 | 26 | 3、CSRF防御:org.springframework.security.web.csrf.InvalidCsrfTokenException: Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'. 27 | 答:thymeleaf 在POST表单中添加即可 28 | 29 | 如果对第三方开放接口,那么以上方式就不能用了,需要添加排除CSRF防御的列表 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /springsecurity4/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /springsecurity4/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /springsecurity4/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cn.geekview 7 | springsecurity4 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | springsecurity4 12 | Demo project for Spring Boot With Spring Security For Registration And Login 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-mail 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-thymeleaf 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-validation 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-devtools 52 | runtime 53 | 54 | 55 | mysql 56 | mysql-connector-java 57 | runtime 58 | 59 | 60 | org.projectlombok 61 | lombok 62 | true 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | test 68 | 69 | 70 | org.springframework.security 71 | spring-security-test 72 | test 73 | 74 | 75 | joda-time 76 | joda-time 77 | 78 | 79 | org.springframework.security 80 | spring-security-taglibs 81 | 4.2.3.RELEASE 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-maven-plugin 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/Springsecurity4Application.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @EnableAutoConfiguration 9 | public class Springsecurity4Application { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Springsecurity4Application.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/annotation/EmailCheck.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.annotation; 2 | 3 | import javax.validation.Constraint; 4 | import java.lang.annotation.*; 5 | 6 | @Target({ElementType.FIELD}) 7 | @Retention(RetentionPolicy.RUNTIME) 8 | @Constraint(validatedBy = EmailChek.class) 9 | @Documented 10 | public @interface EmailCheck { 11 | 12 | String message() default "非法邮箱地址"; 13 | } 14 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/annotation/EmailChek.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.annotation; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class EmailChek implements ConstraintValidator { 9 | 10 | private Pattern pattern; 11 | private Matcher matcher; 12 | private static final String EMAIL_PATTERN = "[A-Za-z0-9._%-+]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; 13 | 14 | @Override 15 | public void initialize(EmailCheck constraintAnnotation) { 16 | 17 | } 18 | 19 | @Override 20 | public boolean isValid(String email, ConstraintValidatorContext context) { 21 | return (validateEmail(email)); 22 | } 23 | 24 | private boolean validateEmail(String email) { 25 | pattern = Pattern.compile(EMAIL_PATTERN); 26 | matcher = pattern.matcher(email); 27 | return matcher.matches(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/config/security/AccessDecisionManagerImpl.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config.security; 2 | 3 | import org.springframework.security.access.AccessDecisionManager; 4 | import org.springframework.security.access.AccessDeniedException; 5 | import org.springframework.security.access.ConfigAttribute; 6 | import org.springframework.security.access.SecurityConfig; 7 | import org.springframework.security.authentication.InsufficientAuthenticationException; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.Collection; 13 | import java.util.Iterator; 14 | 15 | /** 16 | * 判断是否有权限 17 | */ 18 | @Component 19 | public class AccessDecisionManagerImpl implements AccessDecisionManager { 20 | 21 | public void decide( Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{ 22 | if( configAttributes == null ) { 23 | return ; 24 | } 25 | Iterator iterator = configAttributes.iterator(); 26 | while( iterator.hasNext()){ 27 | ConfigAttribute configAttribute = iterator.next(); 28 | String needRole = ((SecurityConfig)configAttribute).getAttribute(); 29 | //grantedAuthority 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。 30 | for( GrantedAuthority grantedAuthority: authentication.getAuthorities()){ 31 | if(needRole.trim().equals(grantedAuthority.getAuthority().trim())){ 32 | return; 33 | } 34 | } 35 | } 36 | /** 37 | * 如果一个AccessDeniedException被抛出并且用户已经被认证,那么这意味着一个操作已经尝试了它们不具有足够的权限。 38 | * 在这种情况下,ExceptionTranslationFilter将调用第二策略,AccessDeniedHandler。 39 | * 默认情况下,AccessDeniedHandlerImpl被使用,这只是发送一个403(禁止)响应于客户端。 40 | * 此外,还可以配置明确的实例,并设置一个错误页面的URL,它会请求转发 . 41 | * 这可以是一个简单的“拒绝访问”页上,如一个JSP,或者它可以是更复杂的处理程序,如一个MVC的控制器。 42 | * 当然,你可以自己实现接口,并使用自己的实现。 43 | */ 44 | throw new AccessDeniedException("权限不足"); 45 | } 46 | 47 | @Override 48 | public boolean supports(ConfigAttribute attribute) { 49 | return true; 50 | } 51 | 52 | @Override 53 | public boolean supports(Class clazz) { 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/config/security/CSRFSecurityRequestMatcher.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config.security; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.security.web.util.matcher.RequestMatcher; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.util.Arrays; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | /** 12 | * 自定义CSRF匹配,排除不需要CSRF防御的资源,比如给第三方开放的接口 13 | * 默认使用的是DefaultRequiresCsrfMatcher 14 | */ 15 | @Slf4j 16 | public class CSRFSecurityRequestMatcher implements RequestMatcher { 17 | 18 | private final HashSet allowedMethods = new HashSet( 19 | Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS")); 20 | //需要排除URL 21 | private Set excludedUrls ; 22 | 23 | @Override 24 | public boolean matches(HttpServletRequest request) { 25 | if (excludedUrls!=null && excludedUrls.size()>0){ 26 | String seveletPath = request.getServletPath(); 27 | for (String excludedUrl : excludedUrls) { 28 | if (seveletPath.contains(excludedUrl)){ 29 | return false; 30 | } 31 | } 32 | } 33 | return !this.allowedMethods.contains(request.getMethod()); 34 | } 35 | 36 | public Set getExcludedUrls() { 37 | return excludedUrls; 38 | } 39 | 40 | public void setExcludedUrls(Set excludedUrls) { 41 | this.excludedUrls = excludedUrls; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/config/security/FilterInvocationSecurityMetadataSourceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config.security; 2 | 3 | import cn.geekview.domain.entity.Resource; 4 | import cn.geekview.domain.entity.Role; 5 | import cn.geekview.domain.repository.ResourceRepository; 6 | import cn.geekview.domain.repository.RoleRepository; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.access.ConfigAttribute; 9 | import org.springframework.security.access.SecurityConfig; 10 | import org.springframework.security.web.FilterInvocation; 11 | import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; 12 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 13 | import org.springframework.security.web.util.matcher.RequestMatcher; 14 | import org.springframework.stereotype.Component; 15 | 16 | import javax.annotation.PostConstruct; 17 | import java.util.*; 18 | 19 | /** 20 | * 处理资源与角色之间的关系:系统在启动的时候就要加载资源与角色的数据 21 | */ 22 | @Component 23 | public class FilterInvocationSecurityMetadataSourceImpl implements FilterInvocationSecurityMetadataSource { 24 | 25 | @Autowired 26 | private RoleRepository roleRepository; 27 | 28 | @Autowired 29 | private ResourceRepository resourceRepository; 30 | 31 | private static Map> resourceMap = null; 32 | 33 | @PostConstruct 34 | private void loadResourceDefine() { 35 | // 在Web服务器启动时,提取系统中的所有权限。 36 | List roleList =roleRepository.findAll(); 37 | 38 | List roleNameList = new ArrayList(); 39 | if(roleList!=null && roleList.size()>0) { 40 | for (Role role : roleList) { 41 | roleNameList.add(role.getRoleName()); 42 | } 43 | } 44 | /* 45 | * 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 46 | * 一个资源可以由多个权限来访问。 47 | * 48 | */ 49 | resourceMap = new HashMap>(); 50 | for (String roleName : roleNameList) { 51 | // 将角色名封装一个安全配置???这样理解有点不准确, 说白了就是一个角色名,只是为了和框架整合换一种说法 52 | ConfigAttribute configAttribute = new SecurityConfig(roleName); 53 | List urlList = new ArrayList(); 54 | List resourceList = resourceRepository.findByRoleName(roleName); 55 | if(resourceList!=null && resourceList.size()>0) {//如果不加判断,这里如果 resourceRepository.findByRoleName(roleName);为null.则会报错 56 | for(Resource resource :resourceList){ 57 | urlList.add(resource.getResource()); 58 | } 59 | } 60 | for (String res : urlList) { 61 | String url = res; 62 | /* 63 | * 判断资源文件和权限的对应关系,如果已经存在相关的资源url, 64 | * 则要通过该url为key提取出权限集合,将权限增加到权限集合中。 65 | * 66 | */ 67 | if (resourceMap.containsKey(url)) { 68 | Collection value = resourceMap.get(url); 69 | value.add(configAttribute); 70 | resourceMap.put(url, value); 71 | } else { 72 | Collection atts = new ArrayList(); 73 | atts.add(configAttribute); 74 | resourceMap.put(url, atts);// 一个URL对应多种角色 75 | } 76 | } 77 | } 78 | 79 | } 80 | 81 | @Override 82 | public Collection getAttributes(Object object) throws IllegalArgumentException { 83 | // object 是一个URL,被用户请求的url。 84 | FilterInvocation filterInvocation = (FilterInvocation) object; 85 | if (resourceMap == null) { 86 | loadResourceDefine(); 87 | } 88 | Iterator iterator = resourceMap.keySet().iterator(); 89 | while (iterator.hasNext()) { 90 | String resURL = iterator.next(); 91 | // 优化请求路径后面带参数的部分 92 | RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL); 93 | if(requestMatcher.matches(filterInvocation.getHttpRequest())) { 94 | return resourceMap.get(resURL); 95 | } 96 | } 97 | return null; 98 | } 99 | 100 | @Override 101 | public Collection getAllConfigAttributes() { 102 | // 根据DefaultFilterInvocationSecurityMetadataSource写的 103 | Set allAttributes = new HashSet(); 104 | for (Map.Entry> entry : resourceMap.entrySet()) { 105 | allAttributes.addAll(entry.getValue()); 106 | } 107 | return allAttributes; 108 | } 109 | 110 | @Override 111 | public boolean supports(Class clazz) { 112 | return true; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/config/security/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.config.security; 2 | 3 | import cn.geekview.handler.LoginFailureHandler; 4 | import cn.geekview.handler.LoginSuccessHandler; 5 | import cn.geekview.service.IUserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.security.authentication.AuthenticationManager; 10 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | import org.springframework.security.crypto.password.PasswordEncoder; 17 | import org.springframework.security.web.AuthenticationEntryPoint; 18 | import org.springframework.security.web.WebAttributes; 19 | import org.springframework.security.web.access.AccessDeniedHandlerImpl; 20 | import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; 21 | import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; 22 | import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; 23 | import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; 24 | import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices; 25 | import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; 26 | import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; 27 | 28 | import javax.servlet.RequestDispatcher; 29 | import javax.servlet.ServletException; 30 | import javax.servlet.http.HttpServletRequest; 31 | import javax.servlet.http.HttpServletResponse; 32 | import javax.sql.DataSource; 33 | import java.io.IOException; 34 | import java.util.HashSet; 35 | import java.util.Set; 36 | 37 | @Configuration 38 | @EnableWebSecurity 39 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 40 | 41 | @Autowired 42 | private IUserService userService; 43 | 44 | @Override 45 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 46 | auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); 47 | } 48 | 49 | @Override 50 | protected void configure(HttpSecurity http) throws Exception { 51 | http 52 | // .addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)//在正确的位置添加我们自定义的过滤器 53 | // 重新添加拥有自己属性的过滤器; 54 | /** 55 | * 不使用自定义过滤器类的话,可以直接使用默认实现的类,并提供自定义的属性 56 | */ 57 | .addFilter(filterSecurityInterceptor()) 58 | .authorizeRequests() 59 | //路径/home不需要验证 60 | .antMatchers("/home").permitAll() 61 | //任何请求都需要授权 62 | .anyRequest().authenticated() 63 | .and() 64 | .formLogin() 65 | .loginPage("/login")//之所以加true 是因为 th:if{param.error} 会去读取浏览器地址携带的参数,有了true之后,if就成立,所以后面的th:text就能执行。 66 | .permitAll() 67 | //登录失败处理 68 | .failureHandler(loginFailureHandler()) 69 | //登录成功处理 70 | .successHandler(loginSuccessHandler()) 71 | .and() 72 | .logout() 73 | .permitAll() 74 | //注销后使session相关信息无效 75 | .invalidateHttpSession(true) 76 | .and() 77 | // 开启rememberme功能:验证,登录成功后,关闭页面,直接访问登陆后可以访问的页面 78 | .rememberMe() 79 | //持久化到数据库 如果不需要持久化到数据库,直接注释掉即可 80 | .rememberMeServices(new PersistentTokenBasedRememberMeServices("MySpringSecurityCookie",userService,persistentTokenRepository())) 81 | //设置有效时间 82 | .tokenValiditySeconds(7*24*60*60) 83 | .and() 84 | .csrf() 85 | //自定义匹配器,方便排除那些不需要csrf防御的地址 86 | .requireCsrfProtectionMatcher(csrfSecurityRequestMatcher()) 87 | .csrfTokenRepository(new HttpSessionCsrfTokenRepository()); 88 | /** 89 | * 处理AccessDeniedException 且用户不是匿名用户 90 | * 例如:USER角色访问ADMIN角色的资源,提示权限不足 91 | */ 92 | http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl()).accessDeniedPage("/deny"); 93 | /** 94 | * 1、处理AuthenticationException 95 | * 2、处理AccessDeniedException 且用户是匿名用户 96 | * 例如:用户注册之后没有激活提示DisabledException 97 | */ 98 | http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()); 99 | } 100 | 101 | @Bean 102 | public AuthenticationEntryPoint authenticationEntryPoint(){ 103 | return new AuthenticationEntryPoint() { 104 | String errorPage = "/deny"; 105 | @Override 106 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 107 | System.out.println(authException.getMessage()); 108 | if (!response.isCommitted()) { 109 | if (errorPage != null) { 110 | // Put exception into request scope (perhaps of use to a view) 111 | request.setAttribute(WebAttributes.ACCESS_DENIED_403, 112 | authException); 113 | 114 | // Set the 403 status code. 115 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 116 | 117 | // forward to error page. 118 | RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage); 119 | dispatcher.forward(request, response); 120 | } 121 | else { 122 | response.sendError(HttpServletResponse.SC_FORBIDDEN, 123 | authException.getMessage()); 124 | } 125 | } 126 | } 127 | }; 128 | } 129 | 130 | @Autowired 131 | private DataSource dataSource; 132 | 133 | @Bean 134 | public PersistentTokenRepository persistentTokenRepository(){ 135 | JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); 136 | // 设置数据源 默认使用的Apache的连接池 137 | jdbcTokenRepository.setDataSource(dataSource); 138 | // 设置初始化存储Token的表 方便调试 由于源码没有对数据库中是否有表结构做出判断,正常使用的时候不建议开启,不然第二次启动会报错! 139 | // jdbcTokenRepository.setCreateTableOnStartup(true); 140 | return jdbcTokenRepository; 141 | } 142 | 143 | @Bean 144 | public PasswordEncoder passwordEncoder(){ 145 | return new BCryptPasswordEncoder(); 146 | } 147 | 148 | @Bean 149 | public LoginSuccessHandler loginSuccessHandler(){ 150 | return new LoginSuccessHandler(); 151 | } 152 | 153 | @Bean 154 | public LoginFailureHandler loginFailureHandler(){ 155 | return new LoginFailureHandler(); 156 | } 157 | 158 | @Autowired 159 | private FilterInvocationSecurityMetadataSourceImpl securityMetadataSource; 160 | 161 | @Autowired 162 | private AccessDecisionManagerImpl accessDecisionManager; 163 | 164 | @Autowired 165 | private AuthenticationManager authenticationManager; 166 | 167 | @Bean 168 | public FilterSecurityInterceptor filterSecurityInterceptor(){ 169 | FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor(); 170 | filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource); 171 | filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager); 172 | filterSecurityInterceptor.setAuthenticationManager(authenticationManager); 173 | return filterSecurityInterceptor; 174 | } 175 | 176 | @Bean 177 | public CSRFSecurityRequestMatcher csrfSecurityRequestMatcher(){ 178 | Set excludedUrls = new HashSet<>(); 179 | excludedUrls.add("/vip/"); 180 | CSRFSecurityRequestMatcher csrfSecurityRequestMatcher = new CSRFSecurityRequestMatcher(); 181 | csrfSecurityRequestMatcher.setExcludedUrls(excludedUrls); 182 | return csrfSecurityRequestMatcher; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | @Controller 9 | public class IndexController { 10 | 11 | @RequestMapping(value = {"/","/home"}) 12 | public String home(){ 13 | return "home"; 14 | } 15 | 16 | @RequestMapping(value = {"/admin"}) 17 | public String admin(){ 18 | return "admin"; 19 | } 20 | 21 | @RequestMapping(value = {"/user"}) 22 | public String user(){ 23 | return "user"; 24 | } 25 | 26 | @RequestMapping(value = {"/vip"}) 27 | public String vip(){ 28 | return "vip"; 29 | } 30 | 31 | @RequestMapping(value = {"/super"}) 32 | public String superm(){ 33 | return "super"; 34 | } 35 | 36 | @RequestMapping(value = {"/deny"}) 37 | public String deny(){ 38 | return "deny"; 39 | } 40 | 41 | @GetMapping("/regist") 42 | public String regist(){ 43 | return "regist"; 44 | } 45 | 46 | @GetMapping("/login") 47 | public String login(){ 48 | return "login"; 49 | } 50 | 51 | @GetMapping("/CSRFTest") 52 | public String product(){ 53 | return "CSRFTest"; 54 | } 55 | 56 | @PostMapping("/CSRFTestcheck") 57 | public String product1(){ 58 | return "home"; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.controller; 2 | 3 | import cn.geekview.domain.dto.RegistraFormDTO; 4 | import cn.geekview.domain.entity.User; 5 | import cn.geekview.domain.entity.ValidateToken; 6 | import cn.geekview.event.OnRegistrationCompleteEvent; 7 | import cn.geekview.service.IUserService; 8 | import cn.geekview.util.ValidateCode; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.joda.time.DateTime; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.context.ApplicationEventPublisher; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.validation.BindingResult; 17 | import org.springframework.web.bind.annotation.GetMapping; 18 | import org.springframework.web.bind.annotation.PostMapping; 19 | import org.springframework.web.bind.annotation.RequestMapping; 20 | import org.springframework.web.context.request.WebRequest; 21 | import org.springframework.web.servlet.ModelAndView; 22 | 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | import javax.servlet.http.HttpSession; 26 | import javax.transaction.Transactional; 27 | import javax.validation.Valid; 28 | 29 | @Controller 30 | @Slf4j 31 | public class UserController { 32 | 33 | @Autowired 34 | private IUserService userService; 35 | 36 | @Autowired 37 | private PasswordEncoder passwordEncoder; 38 | 39 | @Autowired 40 | private ApplicationEventPublisher eventPublisher; 41 | 42 | // 注册 43 | @PostMapping("/registra") 44 | public ModelAndView registra(@Valid RegistraFormDTO registraFormDTO, BindingResult result, WebRequest request){ 45 | // 校验表单字段 46 | if (result.hasErrors()){ 47 | return new ModelAndView("regist","message",result.getFieldError()); 48 | } 49 | // 保存注册信息 50 | User user = new User(); 51 | user.setUsername(registraFormDTO.getUsername()); 52 | user.setPassword(passwordEncoder.encode(registraFormDTO.getPassword())); 53 | user.setEmail(registraFormDTO.getEmail()); 54 | User registrated = userService.creatNewUser(user); 55 | // 发布邮箱验证事件 56 | String appUrl = request.getContextPath(); 57 | if (registrated == null){ 58 | return new ModelAndView("regist","message","该邮箱已经注册!!!"); 59 | } 60 | try { 61 | eventPublisher.publishEvent(new OnRegistrationCompleteEvent(user,appUrl)); 62 | }catch (Exception e){ 63 | log.debug(e.getMessage()); 64 | return new ModelAndView("regist","message","请确认邮箱状态是否正常!!!"); 65 | } 66 | return new ModelAndView("redirect:/login"); 67 | } 68 | /** 69 | * 邮箱验证 70 | * 1、判断token是否存在 71 | * 2、判断是否过期 72 | */ 73 | @GetMapping("/registationConfirm") 74 | public String registationConfirm(String token, Model model){ 75 | ValidateToken validateToken = userService.findValidateToken(token); 76 | if (validateToken == null) { 77 | model.addAttribute("message","请求地址中的Token不存在"); 78 | return "redirect:/regist"; 79 | } 80 | if (validateToken.getExpiryDate().getTime()- DateTime.now().getMillis()<0){ 81 | model.addAttribute("message","请求地址中的Token已过期"); 82 | return "redirect:/regist"; 83 | } 84 | User user = validateToken.getUser(); 85 | user.setEnabled(true); 86 | userService.saveRegistratedUser(user); 87 | return "redirect:/login"; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/dto/RegistraFormDTO.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.dto; 2 | 3 | import lombok.Data; 4 | import javax.validation.constraints.NotNull; 5 | import org.hibernate.validator.constraints.Email; 6 | import org.hibernate.validator.constraints.NotEmpty; 7 | 8 | import javax.validation.constraints.Size; 9 | 10 | /** 11 | * 注册表单数据传输对象 12 | */ 13 | @Data 14 | public class RegistraFormDTO { 15 | 16 | @NotEmpty 17 | @NotNull 18 | @Size(max = 255) 19 | private String username; 20 | 21 | @NotEmpty 22 | @NotNull 23 | @Size(min = 6,max = 32) 24 | private String password; 25 | 26 | @NotEmpty 27 | @NotNull 28 | @Email 29 | private String email; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/entity/Resource.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.entity; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | @Data 11 | @Entity 12 | @Table(name = "t_resource") 13 | public class Resource { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.AUTO) 17 | private Long id; 18 | 19 | private String resource; 20 | 21 | @ManyToMany(fetch = FetchType.LAZY, 22 | cascade = { 23 | CascadeType.PERSIST, 24 | CascadeType.MERGE 25 | }, 26 | mappedBy = "resources") 27 | private Set roles = new HashSet<>(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/entity/Role.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.entity; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | @Data 10 | @Entity 11 | @Table(name = "t_role") 12 | public class Role { 13 | 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.AUTO) 16 | private Long id; 17 | 18 | private String roleName; 19 | 20 | @ManyToMany(targetEntity = User.class,mappedBy = "roles",fetch = FetchType.EAGER) 21 | private Set users = new HashSet<>(); 22 | 23 | @ManyToMany(fetch = FetchType.EAGER, 24 | cascade = { 25 | CascadeType.PERSIST, 26 | CascadeType.MERGE 27 | }) 28 | @JoinTable(name = "t_role_resorces", 29 | joinColumns = { @JoinColumn(name = "roles_id") }, 30 | inverseJoinColumns = { @JoinColumn(name = "resources_id") }) 31 | private Set resources = new HashSet<>(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/entity/User.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.entity; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | import javax.persistence.*; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * 这里不能使用lombok @Data注解 15 | * 否则会出现@ManyToMany死循环导致内存溢出 16 | * 否则就是org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.geekview.domain.entity.User.roles, could not initialize proxy - no Session 17 | * 18 | * ??? 19 | * 20 | */ 21 | @Entity 22 | @Table(name = "t_user") 23 | public class User implements UserDetails{ 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.AUTO) 27 | private Long id; 28 | 29 | private String username; 30 | 31 | private String password; 32 | 33 | private String email; 34 | 35 | /* 36 | AbstractUserDetailsAuthenticationProvider的私有内部类DefaultPreAuthenticationChecks会对用户是否可用进行校验 37 | */ 38 | private boolean enabled; 39 | 40 | public User(){ 41 | this.enabled = false; 42 | } 43 | 44 | /** 45 | * CascadeType.DETACH 46 | Cascade detach operation,级联脱管/游离操作。 47 | 如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联。 48 | */ 49 | @ManyToMany(targetEntity = Role.class,fetch = FetchType.EAGER) 50 | private Set roles = new HashSet<>(); 51 | 52 | 53 | @Override 54 | public Collection getAuthorities() { 55 | Collection grantedAuthorities = new ArrayList<>(); 56 | for (Role role : roles) { 57 | SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getRoleName()); 58 | grantedAuthorities.add(simpleGrantedAuthority); 59 | } 60 | return grantedAuthorities; 61 | } 62 | 63 | @Override 64 | public boolean isAccountNonExpired() { 65 | return true; 66 | } 67 | 68 | @Override 69 | public boolean isAccountNonLocked() { 70 | return true; 71 | } 72 | 73 | @Override 74 | public boolean isCredentialsNonExpired() { 75 | return true; 76 | } 77 | 78 | public Long getId() { 79 | return id; 80 | } 81 | 82 | public void setId(Long id) { 83 | this.id = id; 84 | } 85 | 86 | @Override 87 | public String getUsername() { 88 | return username; 89 | } 90 | 91 | public void setUsername(String username) { 92 | this.username = username; 93 | } 94 | 95 | @Override 96 | public String getPassword() { 97 | return password; 98 | } 99 | 100 | public void setPassword(String password) { 101 | this.password = password; 102 | } 103 | 104 | public String getEmail() { 105 | return email; 106 | } 107 | 108 | public void setEmail(String email) { 109 | this.email = email; 110 | } 111 | 112 | public Set getRoles() { 113 | return roles; 114 | } 115 | 116 | public void setRoles(Set roles) { 117 | this.roles = roles; 118 | } 119 | 120 | @Override 121 | public boolean isEnabled() { 122 | return enabled; 123 | } 124 | 125 | public void setEnabled(boolean enabled) { 126 | this.enabled = enabled; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/entity/ValidateToken.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.entity; 2 | 3 | import lombok.Data; 4 | import org.joda.time.DateTime; 5 | 6 | import javax.persistence.*; 7 | import java.util.Date; 8 | 9 | /** 10 | * 与注册用户一一对应 11 | */ 12 | @Data 13 | @Entity 14 | @Table(name = "t_validatetoken") 15 | public class ValidateToken { 16 | 17 | private final static int EXPIRY = 24*60; 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.AUTO) 21 | private Long id; 22 | 23 | /** 24 | * @OneToOne 注释只能确定实体与实体的关系是一对一的关系, 25 | * 不能指定数据库表中的保存的关联字段。 26 | * 所以此时要结合@JoinColumn标记来指定保存实体关系的配置。 27 | */ 28 | @OneToOne(targetEntity = User.class,fetch = FetchType.EAGER) 29 | @JoinColumn(nullable = false,name = "user_id") 30 | private User user; 31 | 32 | private String token; 33 | 34 | private Date expiryDate; 35 | 36 | public ValidateToken(User user,String token){ 37 | this.user = user; 38 | this.token = token; 39 | this.expiryDate = DateTime.now().plusMinutes(EXPIRY).toDate(); 40 | } 41 | 42 | public ValidateToken(){ 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/repository/ResourceRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.repository; 2 | 3 | import cn.geekview.domain.entity.Resource; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | 9 | public interface ResourceRepository extends JpaRepository { 10 | 11 | @Query(value = "SELECT * FROM t_resource WHERE id IN ( SELECT resources_id FROM t_role_resources WHERE roles_id = ( SELECT id FROM t_role WHERE role_name = ?1))",nativeQuery = true) 12 | List findByRoleName(String rolename); 13 | } 14 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/repository/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.repository; 2 | 3 | import cn.geekview.domain.entity.Role; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface RoleRepository extends JpaRepository{ 7 | } 8 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.repository; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface UserRepository extends JpaRepository { 7 | 8 | User findByUsername(String username); 9 | 10 | User findByEmail(String eamil); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/domain/repository/ValidateTokenRepository.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.domain.repository; 2 | 3 | import cn.geekview.domain.entity.ValidateToken; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface ValidateTokenRepository extends JpaRepository { 7 | 8 | ValidateToken findByToken(String token); 9 | } 10 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/event/OnRegistrationCompleteEvent.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.event; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import org.springframework.context.ApplicationEvent; 5 | 6 | 7 | public class OnRegistrationCompleteEvent extends ApplicationEvent { 8 | 9 | private final String appUrl; 10 | 11 | private final User user; 12 | 13 | 14 | public OnRegistrationCompleteEvent(final User user, final String appUrl) { 15 | super(user); 16 | this.user = user; 17 | this.appUrl = appUrl; 18 | } 19 | 20 | public String getAppUrl() { 21 | return appUrl; 22 | } 23 | 24 | 25 | public User getUser() { 26 | return user; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/event/listener/RegistrationListener.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.event.listener; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import cn.geekview.domain.entity.ValidateToken; 5 | import cn.geekview.event.OnRegistrationCompleteEvent; 6 | import cn.geekview.service.IUserService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.ApplicationListener; 9 | import org.springframework.core.env.Environment; 10 | import org.springframework.mail.SimpleMailMessage; 11 | import org.springframework.mail.javamail.JavaMailSender; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.UUID; 15 | 16 | /** 17 | * 1、生成并保存邮箱验证令牌 18 | * 2、发送验证邮件 19 | */ 20 | @Component 21 | public class RegistrationListener implements ApplicationListener { 22 | 23 | @Autowired 24 | private JavaMailSender mailSender; 25 | 26 | @Autowired 27 | private IUserService userService; 28 | 29 | @Autowired 30 | private Environment environment; 31 | 32 | @Override 33 | public void onApplicationEvent(OnRegistrationCompleteEvent event) { 34 | User user = event.getUser(); 35 | String appurl = event.getAppUrl(); 36 | String token = UUID.randomUUID().toString(); 37 | ValidateToken validateToken = new ValidateToken(user,token); 38 | userService.saveValidateToken(validateToken); 39 | 40 | SimpleMailMessage mailMessage = new SimpleMailMessage(); 41 | mailMessage.setSubject("注册确认邮件"); 42 | mailMessage.setText("http://localhost:80"+appurl+"/registationConfirm?token="+token); 43 | mailMessage.setTo(user.getEmail()); 44 | mailMessage.setFrom(environment.getProperty("mail.from.addr")); 45 | 46 | mailSender.send(mailMessage); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/handler/LoginFailureHandler.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.handler; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | import org.springframework.security.web.DefaultRedirectStrategy; 5 | import org.springframework.security.web.RedirectStrategy; 6 | import org.springframework.security.web.WebAttributes; 7 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 8 | 9 | import javax.servlet.ServletException; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | public class LoginFailureHandler implements AuthenticationFailureHandler{ 15 | 16 | private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 17 | 18 | @Override 19 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { 20 | System.out.println("++++++++++++++>"+exception); 21 | if ("User is disabled".equals(exception.getMessage())){ 22 | request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, "用户没有激活,请激活!"); 23 | }else { 24 | request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); 25 | } 26 | redirectStrategy.sendRedirect(request,response,"/login"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/handler/LoginSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.handler; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import cn.geekview.domain.repository.ResourceRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.web.DefaultRedirectStrategy; 9 | import org.springframework.security.web.RedirectStrategy; 10 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 11 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 12 | 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | 18 | /** 19 | * 登录成功处理器 20 | * 需求: 21 | * 登录成功后,默认跳转到对应角色下的页面 22 | */ 23 | public class LoginSuccessHandler implements AuthenticationSuccessHandler { 24 | 25 | private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 26 | 27 | @Override 28 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, 29 | ServletException { 30 | System.out.println(authentication); 31 | 32 | System.out.println(authentication.getPrincipal());//用户对象 33 | //获得授权后可得到用户信息 34 | User user = (User) authentication.getPrincipal(); 35 | 36 | //输出登录提示信息 37 | System.out.println("用户名: " + user.getUsername()); 38 | 39 | System.out.println("用户密码: " + authentication.getCredentials()); 40 | 41 | System.out.println("角色列表:"+authentication.getAuthorities()); 42 | 43 | System.out.println("IP信息 :"+authentication.getDetails()); 44 | 45 | System.out.println("是否被授权 :"+authentication.isAuthenticated()); 46 | 47 | //登录成功后跳转到默认对应的页面 48 | String targetUrl = "/home"; 49 | for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { 50 | String roleName = grantedAuthority.getAuthority(); 51 | switch (roleName){ 52 | case "SUPER": targetUrl = "/super";break; 53 | case "ADMIN": targetUrl = "/admin";break; 54 | case "VIP": targetUrl = "/vip";break; 55 | case "USER": targetUrl = "/user";break; 56 | } 57 | } 58 | redirectStrategy.sendRedirect(request,response,targetUrl); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import cn.geekview.domain.entity.ValidateToken; 5 | import org.springframework.security.core.userdetails.UserDetailsService; 6 | 7 | public interface IUserService extends UserDetailsService{ 8 | 9 | User creatNewUser(User user); 10 | 11 | void saveValidateToken(ValidateToken token); 12 | 13 | ValidateToken findValidateToken(String token); 14 | 15 | void saveRegistratedUser(User user); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.service.impl; 2 | 3 | import cn.geekview.domain.entity.User; 4 | import cn.geekview.domain.entity.ValidateToken; 5 | import cn.geekview.domain.repository.UserRepository; 6 | import cn.geekview.domain.repository.ValidateTokenRepository; 7 | import cn.geekview.service.IUserService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.security.authentication.DisabledException; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 13 | import org.springframework.stereotype.Service; 14 | 15 | import javax.transaction.Transactional; 16 | 17 | @Service 18 | @Slf4j 19 | @Transactional 20 | public class UserServiceImpl implements IUserService { 21 | 22 | @Autowired 23 | private UserRepository userRepository; 24 | 25 | @Autowired 26 | private ValidateTokenRepository tokenRepository; 27 | 28 | /** 29 | * 邮箱登录,邮箱地址替换用户名称 30 | * @param email 31 | * @return 32 | * @throws UsernameNotFoundException 33 | */ 34 | @Override 35 | public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { 36 | User user = userRepository.findByEmail(email); 37 | if (user == null) { 38 | throw new UsernameNotFoundException("用户名不存在"); 39 | } 40 | return user; 41 | 42 | } 43 | 44 | /** 45 | * 创建一个新的账户 46 | * @param user 47 | * @return 48 | */ 49 | @Override 50 | public User creatNewUser(User user) { 51 | if (isEmailExist(user.getEmail())){ 52 | return null; 53 | }else { 54 | return userRepository.save(user); 55 | } 56 | } 57 | 58 | /** 59 | * 保存验证令牌 60 | * @param validateToken 61 | */ 62 | @Override 63 | public void saveValidateToken(ValidateToken validateToken) { 64 | tokenRepository.save(validateToken); 65 | } 66 | 67 | @Override 68 | public ValidateToken findValidateToken(String token) { 69 | return tokenRepository.findByToken(token); 70 | } 71 | 72 | @Override 73 | public void saveRegistratedUser(User user) { 74 | userRepository.save(user); 75 | } 76 | 77 | /** 78 | * 判断邮箱是否已经存在 79 | * @param email 80 | * @return 81 | */ 82 | public boolean isEmailExist(String email){ 83 | User user = userRepository.findByEmail(email); 84 | if (user==null){ 85 | return false; 86 | }else { 87 | return true; 88 | } 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /springsecurity4/src/main/java/cn/geekview/util/ValidateCode.java: -------------------------------------------------------------------------------- 1 | package cn.geekview.util; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.*; 5 | import java.awt.image.BufferedImage; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.util.Date; 10 | import java.util.Random; 11 | 12 | /** 13 | * 验证码生成器 14 | * 15 | * @author 16 | */ 17 | public class ValidateCode { 18 | // 图片的宽度。 19 | private int width = 160; 20 | // 图片的高度。 21 | private int height = 40; 22 | // 验证码字符个数 23 | private int codeCount = 5; 24 | // 验证码干扰线数 25 | private int lineCount = 150; 26 | // 验证码 27 | private String code = null; 28 | // 验证码图片Buffer 29 | private BufferedImage buffImg = null; 30 | 31 | // 验证码范围,去掉0(数字)和O(拼音)容易混淆的(小写的1和L也可以去掉,大写不用了) 32 | private char[] codeSequence = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 33 | 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 34 | 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; 35 | 36 | /** 37 | * 默认构造函数,设置默认参数 38 | */ 39 | public ValidateCode() { 40 | this.createCode(); 41 | } 42 | 43 | /** 44 | * @param width 图片宽 45 | * @param height 图片高 46 | */ 47 | public ValidateCode(int width, int height) { 48 | this.width = width; 49 | this.height = height; 50 | this.createCode(); 51 | } 52 | 53 | /** 54 | * @param width 图片宽 55 | * @param height 图片高 56 | * @param codeCount 字符个数 57 | * @param lineCount 干扰线条数 58 | */ 59 | public ValidateCode(int width, int height, int codeCount, int lineCount) { 60 | this.width = width; 61 | this.height = height; 62 | this.codeCount = codeCount; 63 | this.lineCount = lineCount; 64 | this.createCode(); 65 | } 66 | 67 | public void createCode() { 68 | int x = 0, fontHeight = 0, codeY = 0; 69 | int red = 0, green = 0, blue = 0; 70 | 71 | x = width / (codeCount + 2);//每个字符的宽度(左右各空出一个字符) 72 | fontHeight = height - 2;//字体的高度 73 | codeY = height - 4; 74 | 75 | // 图像buffer 76 | buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 77 | Graphics2D g = buffImg.createGraphics(); 78 | // 生成随机数 79 | Random random = new Random(); 80 | // 将图像填充为白色 81 | g.setColor(Color.WHITE); 82 | g.fillRect(0, 0, width, height); 83 | // 创建字体,可以修改为其它的 84 | Font font = new Font("Fixedsys", Font.PLAIN, fontHeight); 85 | // Font font = new Font("Times New Roman", Font.ROMAN_BASELINE, fontHeight); 86 | g.setFont(font); 87 | 88 | for (int i = 0; i < lineCount; i++) { 89 | // 设置随机开始和结束坐标 90 | int xs = random.nextInt(width);//x坐标开始 91 | int ys = random.nextInt(height);//y坐标开始 92 | int xe = xs + random.nextInt(width / 8);//x坐标结束 93 | int ye = ys + random.nextInt(height / 8);//y坐标结束 94 | 95 | // 产生随机的颜色值,让输出的每个干扰线的颜色值都将不同。 96 | red = random.nextInt(255); 97 | green = random.nextInt(255); 98 | blue = random.nextInt(255); 99 | g.setColor(new Color(red, green, blue)); 100 | g.drawLine(xs, ys, xe, ye); 101 | } 102 | 103 | // randomCode记录随机产生的验证码 104 | StringBuffer randomCode = new StringBuffer(); 105 | // 随机产生codeCount个字符的验证码。 106 | for (int i = 0; i < codeCount; i++) { 107 | String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]); 108 | // 产生随机的颜色值,让输出的每个字符的颜色值都将不同。 109 | red = random.nextInt(255); 110 | green = random.nextInt(255); 111 | blue = random.nextInt(255); 112 | g.setColor(new Color(red, green, blue)); 113 | g.drawString(strRand, (i + 1) * x, codeY); 114 | // 将产生的四个随机数组合在一起。 115 | randomCode.append(strRand); 116 | } 117 | // 将四位数字的验证码保存到Session中。 118 | code = randomCode.toString(); 119 | } 120 | 121 | public void write(String path) throws IOException { 122 | OutputStream sos = new FileOutputStream(path); 123 | this.write(sos); 124 | } 125 | 126 | public void write(OutputStream sos) throws IOException { 127 | ImageIO.write(buffImg, "png", sos); 128 | sos.close(); 129 | } 130 | 131 | public BufferedImage getBuffImg() { 132 | return buffImg; 133 | } 134 | 135 | public String getCode() { 136 | return code; 137 | } 138 | 139 | /** 140 | * 测试函数,默认生成到d盘 141 | * @param args 142 | */ 143 | public static void main(String[] args) { 144 | ValidateCode vCode = new ValidateCode(160,40,5,150); 145 | try { 146 | String path="C:/"+new Date().getTime()+".png"; 147 | System.out.println(vCode.getCode()+" >"+path); 148 | vCode.write(path); 149 | } catch (IOException e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ScnuWang/SpringBoot/dab0a4c95389a69f3dbaadb09d28741e4d706c66/springsecurity4/src/main/resources/application.properties -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/CSRFTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CSRF测试 5 | 6 | 7 | 8 | 这里是CSRF测试页!!! 9 | 10 |
11 | 邮 箱: 12 | 密 码: 13 | 14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ADMIN 5 | 6 | 7 | 8 | 这里是ADMIN页面!!!!! 9 | 10 | 访问: 11 | 首页 12 | 普通用户页面 13 | VIP用户页面 14 | 管理员页面 15 | 超级管理员页面 16 | 17 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/deny.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 403 5 | 6 | 7 | 8 | 9 | 请求异常信息: 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HOME 5 | 6 | 7 | 8 | 这里是首页!!! 9 | 10 | 11 |
12 | 13 |
14 | 15 | 访问: 16 | 首页 17 | 普通用户页面 18 | VIP用户页面 19 | 管理员页面 20 | 超级管理员页面 21 | 22 | 注册 23 | 登录 24 | 25 | 26 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 登录 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 邮 箱: 29 | 密 码: 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/regist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 注册 5 | 6 | 7 | 8 |
9 | 用户名: 10 | 密 码: 11 | 邮 箱: 12 | 13 |
14 | 15 | 异常信息 16 | 17 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/super.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SUPER 5 | 6 | 7 | 8 | 9 | 这里是SUPER用户的页面!!! 10 | 11 | 访问: 12 | 首页 13 | 普通用户页面 14 | VIP用户页面 15 | 管理员页面 16 | 超级管理员页面 17 | 18 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/user.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | USER 5 | 6 | 7 | 8 | 这里是普通注册用户的页面!!! 9 | 访问: 10 | 首页 11 | 普通用户页面 12 | VIP用户页面 13 | 管理员页面 14 | 超级管理员页面 15 | 16 | 17 | -------------------------------------------------------------------------------- /springsecurity4/src/main/resources/templates/vip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VIP 5 | 6 | 7 | 8 | 9 | 这里是VIP页面!!! 10 | 11 | 访问: 12 | 首页 13 | 普通用户页面 14 | VIP用户页面 15 | 管理员页面 16 | 超级管理员页面 17 | 18 | -------------------------------------------------------------------------------- /springsecurity4/src/test/java/cn/geekview/Springsecurity4ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.geekview; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class Springsecurity4ApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------