SpringBoot整合SpringSecurity
一、添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.4.4</version> </dependency>
创建数据库表
DROP TABLE IF EXISTS `vhr`.`user`; CREATE TABLE `vhr`.`user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(45) NOT NULL DEFAULT '' COMMENT '登录用户名', `password` varchar(255) NOT NULL DEFAULT '' COMMENT '登录密码', `enabled` tinyint(1) NOT NULL DEFAULT '0', `locked` tinyint(1) NOT NULL DEFAULT '0', `name` varchar(32) DEFAULT NULL COMMENT '姓名', `userface` varchar(255) DEFAULT NULL COMMENT '用户头像', `phone` varchar(11) DEFAULT NULL COMMENT '手机号码', `telephone` varchar(16) DEFAULT NULL COMMENT '固定电话', `address` varchar(255) DEFAULT NULL COMMENT '联系地址', `email` varchar(255) DEFAULT NULL COMMENT '电子邮箱', `remark` varchar(255) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `vhr`.`role`; CREATE TABLE `vhr`.`role` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `nameEn` varchar(45) NOT NULL DEFAULT '', `nameCn` varchar(45) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `vhr`.`user_role`; CREATE TABLE `vhr`.`user_role` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `uid` int(10) unsigned NOT NULL DEFAULT '0', `rid` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `FK_user_role_1` (`uid`), KEY `FK_user_role_2` (`rid`), CONSTRAINT `FK_user_role_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`), CONSTRAINT `FK_user_role_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `vhr`.`menu`; CREATE TABLE `vhr`.`menu` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `url` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '请求路径规则', `path` varchar(64) DEFAULT NULL COMMENT '路由path', `component` varchar(64) DEFAULT NULL COMMENT '组件英文名称', `name` varchar(64) DEFAULT NULL COMMENT '组件中文名称', `iconCls` varchar(64) DEFAULT NULL COMMENT '菜单图标', `keepAlive` tinyint(1) NOT NULL DEFAULT '0' COMMENT '菜单切换时是否保活', `requireAuth` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否登录后才能访问', `pid` int(10) unsigned DEFAULT NULL COMMENT '父菜单Id', `enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `vhr`.`menu_role`; CREATE TABLE `vhr`.`menu_role` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `mid` int(10) unsigned NOT NULL DEFAULT '0', `rid` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `FK_menu_role_1` (`mid`), KEY `FK_menu_role_2` (`rid`), CONSTRAINT `FK_menu_role_1` FOREIGN KEY (`mid`) REFERENCES `menu` (`id`), CONSTRAINT `FK_menu_role_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
二、启动项目测试
随便添加一个简单接口,网页url访问测试,我这里用的是之前的接口
默认用户名是user,默认密码在每次启动项目时随机生成
Using generated security password: 0a98ecc8-779b-4dd7-86c6-1b39f6b77437
三、后端代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.22</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit-dep</artifactId> <version>4.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!-- alibaba数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.4.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
package com.example.security.demo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.Collection; //@Component @Service public class DemoUserDetailsService implements UserDetailsService { private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfigurerAdapter.class); // @Resource // private Mapper @Resource private PasswordEncoder passwordEncoder; // @Autowired // UserMapper userMapper; // // 重写loadUserByUsername 方法获得 userdetails 类型用户 // @Override // public UserDetails loadUserByUsername(String username) { // SysUser user = userMapper.findByUserName(username); // if (user != null) { // return user; // } else { // throw new UsernameNotFoundException("admin: " + username + " do not exist!"); // } // } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //根据用户名查找用户信息(通过Mapper查询用户信息) logger.info("============loadUserByUsername========================="+username); //根据查找到的用户信息判断用户是否被冻结 // return new User(username,"123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//authorities:权限 return new User(username,passwordEncoder.encode("123456"), true,true,true,true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//authorities:权限 } // public SecurityUser(User user) { // if (user != null) { // this.setUserUuid(user.getUserUuid()); // this.setUsername(user.getUsername()); // this.setPassword(user.getPassword()); // this.setEmail(user.getEmail()); // this.setTelephone(user.getTelephone()); // this.setRole(user.getRole()); // this.setImage(user.getImage()); // this.setLastIp(user.getLastIp()); // this.setLastTime(user.getLastTime()); // } // } // @Override // public Collection<? extends GrantedAuthority> getAuthorities() { // Collection<GrantedAuthority> authorities = new ArrayList<>(); // String username = this.getUsername(); // if (username != null) { // SimpleGrantedAuthority authority = new SimpleGrantedAuthority(username); // authorities.add(authority); // } // return authorities; // } // //账户是否未过期,过期无法验证 // @Override // public boolean isAccountNonExpired() { // return true; // } // // //指定用户是否解锁,锁定的用户无法进行身份验证 // @Override // public boolean isAccountNonLocked() { // return true; // } // // //指示是否已过期的用户的凭据(密码),过期的凭据防止认证 // @Override // public boolean isCredentialsNonExpired() { // return true; // } // // //是否可用 ,禁用的用户不能身份验证 // @Override // public boolean isEnabled() { // return true; // } }
package com.example.security.demo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.annotation.Resource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Configuration @EnableWebSecurity public class DemoSecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfigurerAdapter.class); @Autowired DemoUserDetailsService demoUserDetailsService; @Bean public PasswordEncoder passwordEncoder(){ // return NoOpPasswordEncoder.getInstance(); return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // auth.userDetailsService(userService); auth.userDetailsService(demoUserDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { logger.info("==============configure=================================="); // super.configure(http); http.csrf().disable(); // http.authorizeRequests() //对请求进行授权 // .antMatchers("/login").permitAll() // .anyRequest() //任何请求 // .authenticated()//都要进行身份认证 // .and() // .formLogin()//表单登录 // .loginPage("/login")//登录页面 // .loginProcessingUrl("/login") // .permitAll();//和登录相关的接口都不需要认证即可访问 http.authorizeRequests(). antMatchers("/static/**").permitAll().anyRequest().authenticated(). and().formLogin().loginPage("/login").permitAll().successHandler(loginSuccessHandler()). and().logout().permitAll().invalidateHttpSession(true). deleteCookies("JSESSIONID").logoutSuccessHandler(logoutSuccessHandler()). and().sessionManagement().maximumSessions(10).expiredUrl("/login"); } //====================================================================== /** * 配置忽略的静态文件,不加的话,登录之前页面的css,js不能正常使用,得登录之后才能正常. */ @Override public void configure(WebSecurity web) throws Exception { // 忽略URL web.ignoring().antMatchers("/**/*.js", "/lang/*.json", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.html", "/**/*.png"); } @Bean public LogoutSuccessHandler logoutSuccessHandler() { //登出处理 return new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { } // @Override // public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { // try { // SecurityUser user = (SecurityUser) authentication.getPrincipal(); //// logger.info("USER : " + user.getUsername() + " LOGOUT SUCCESS ! "); // } catch (Exception e) { //// logger.info("LOGOUT EXCEPTION , e : " + e.getMessage()); // } // httpServletResponse.sendRedirect("/login"); // } }; } @Bean public SavedRequestAwareAuthenticationSuccessHandler loginSuccessHandler() { //登入处理 return new SavedRequestAwareAuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { User userDetails = (User) authentication.getPrincipal(); logger.info("USER : " + userDetails.getUsername() + " LOGIN SUCCESS ! "); super.onAuthenticationSuccess(request, response, authentication); } }; } // @Bean // UserDetailsService demoUserDetailsService() { // return new DemoUserDetailsService(); // } @Bean public UserDetailsService userDetailsService() { //用户登录实现 // return new DemoUserDetailsService(); return new UserDetailsService() { // @Override // public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { // return null; // } // @Autowired // private UserRepository userRepository; @Resource private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.info("=-======loadUserByUsername======-=-========d================"+username); // User user = userRepository.findByUsername(s); // if (user == null) throw new UsernameNotFoundException("Username " + s + " not found"); // return new SecurityUser(user); return new User(username,passwordEncoder.encode("123456"), true,true,true,true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } }; } // @Autowired // public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); // auth.eraseCredentials(false); // } }
package com.example.controller; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Controller public class LoginController { @RequestMapping(value = "/login", method = RequestMethod.GET) public String login() { System.out.println("--------/login------------"); return "/login"; } @RequestMapping("/") public String root() { return "/index"; } // public User getUser() { //为了session从获取用户信息,可以配置如下 // User user = new User(); // SecurityContext ctx = SecurityContextHolder.getContext(); // Authentication auth = ctx.getAuthentication(); // if (auth.getPrincipal() instanceof UserDetails) user = (User) auth.getPrincipal(); // return user; // } public HttpServletRequest getRequest() { return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } }
package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //@SpringBootApplication(scanBasePackages = {"com.example.controller","com.example.service"}) @SpringBootApplication(scanBasePackages = {"com.example"}) @MapperScan("com.example.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
四、前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <h1>登录页面</h1> <form action="/login" method="post"> 用户名 : <input type="text" name="username"/> 密码 : <input type="password" name="password"/> <input type="submit" value="登录"> </form> </body> </html>