长太息以掩涕兮,哀学习之多艰。
自学SpringBoot+SpringSecurity,遇到的问题多多,先记下吧!
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 http://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.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<packaging>war</packaging>
<groupId>com</groupId>
<artifactId>codeStack</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>codeStack</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 移除嵌入式tomcat插件
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions> -->
</dependency>
<!--避坑包-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<!-- 模版引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spring security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>9.0.20</version>
<scope>provided</scope>
</dependency>
<!-- 添加对 JDBC 的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mysql driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<finalName>codestock</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port=8888
spring.mvc.static-path-pattern=/static/**
#thymelea模板配置
#<!-- 关闭thymeleaf缓存 开发时使用 否则没有实时画面-->
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
#thymeleaf end
#数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/codestock?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
#mybatis配置
mybatis.mapper-locations=classpath*:mapper/*.xml
mybatis.type-aliases-package=com.codestock.user.dao
重写WebSecurityConfigurerAdapter,使用自己的验证规则
package com.codeStack.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider provider;
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http
.authorizeRequests()
.antMatchers("/","/js/**","/css/**","/bootstrap/**","/vue/**","/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").successForwardUrl("/code").loginProcessingUrl("/login/form").failureUrl("/login-error").permitAll()
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login").invalidateHttpSession(true).deleteCookies("JSESSIONID")
.and()
.csrf().disable();
super.configure(http);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManagerBean();
}
//自定义用户名、密码的方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
super.configure(auth);
auth.authenticationProvider(provider);
}
}
用户名密码验证:重写AuthenticationProvider
package com.codeStack.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import com.codeStack.util.MD5Util;
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
private final static Logger logger = LoggerFactory.getLogger(MyAuthenticationProvider.class);
@Autowired
private MyUserDetailService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// TODO Auto-generated method stub
String userName = authentication.getName();//获取表单中输入的用户名
String password = (String) authentication.getCredentials();//获取表单中输入的密码
//判断用户是否存在、密码是否正确
User user = (User) userDetailsService.loadUserByUsername(userName);
if(user==null){
logger.error("用户名不存在!!!");
throw new BadCredentialsException("用户名不存在!");
}
//一般此处密码需要做加密处理
String inputPassword = new MD5Util().toMd5(password);
logger.info(user.getUsername()+"登录,正确密码为:"+user.getPassword()+",登录密码为:"+inputPassword);
if(!user.getPassword().equals(inputPassword)){
logger.error("密码不正确!!!");
throw new BadCredentialsException("密码不正确!");
}
//构建用户登录成功返回的token
return new UsernamePasswordAuthenticationToken(user, password,user.getAuthorities());
}
@Override
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}
调用数据库查询:重写UserDetailsService
package com.codeStack.security;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.codeStack.user.dao.UserInfoDao;
import com.codeStack.user.entity.UserInfo;
@Component
public class MyUserDetailService implements UserDetailsService {
private final static Logger logger = LoggerFactory.getLogger(MyUserDetailService.class);
@Autowired
private UserInfoDao userInfoDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
UserInfo userInfo = userInfoDao.selectByUserName(username);
if(userInfo!=null){
//对应的权限添加
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
if(userInfo.getUserName().equals("admin")){
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}else{
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
User user = new User(username, userInfo.getPassword(), authorities);
return user;
}
return null;
}
}
由于使用thymeleaf模板引擎,在其文件夹下的html文件都需要通过controller跳转才能访问
package com.codeStack.user.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.codeStack.security.MyAuthenticationProvider;
@Controller
public class LoginController {
private final static Logger logger = LoggerFactory.getLogger(MyAuthenticationProvider.class);
//配置自己的登录页面
@RequestMapping("/login")
public String login(){
return "login";
}
//登录失败
@RequestMapping("/login-error")
public String loginError(){
logger.info("登录失败....");
return "login-error";
}
//登录成功页面
@RequestMapping("/login/form")
public String loginSuccess(){
logger.info("登录成功....");
return "code";
}
//首页为在售项目页面
@RequestMapping("/")
public String loginAction(){
logger.info("进入首页....");
return "code";
}
//在售项目页面
@RequestMapping("/code")
public String toCode(Map<Object,Object> map){
map.put("msg", "模版引擎测试");
return "code";
}
//预定项目页面
@RequestMapping("/booking")
public String toBooking(){
logger.info("进入预定页面...");
return "booking";
}
//个人中心页面
@RequestMapping("/mine")
public String toMine(){
logger.info("进入个人中心页面...");
return "mine";
}
}
启动项目,访问效果如下
登录成功后如下
存在的问题:
1、登录成功后跳转到首页,但是直接刷新时会再次发送登录请求
2、首页在未登录情况下不能访问,加了.antMatchers("/","/code").permitAll()也不行
3、未登录情况下,static文件夹下的vue/vue.js会被拦截,不知如何解!