代码地址:https://github.com/uzck/spring-security-login-demo
只是一个小的验证登录的demo,按照网上的版本来莫名踩坑,不清楚是不是Spring版本的问题,声明下自己的配置,这是必须的两个包,数据库配置按照自己需要的来。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
<version>2.1.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.1.1.RELEASEversion>
dependency>
在pom.xml的build中添加这部分,保证resource里的资源在打包的时候都被包括进来
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.ymlinclude>
<include>**/*.ftlinclude>
<include>**/*.htmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
这里具体的配置还是按照自己的工程来修改
spring:
datasource:
username: uzck
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/todo?useUnicode=true&characterEncoding=UTF-8
freemarker:
charset: UTF-8
cache: false
template-loader-path: classpath:/templates/
suffix: .ftl
mybatis:
type-aliases-package: security/dao
mapper-locations: classpath:mapper/*.xml
CREATE TABLE `user` (
`username` varchar(255) NOT NULL,
`password` char(255) NOT NULL,
`roles` enum('MEMBER','MEMBER,LEADER','SUPER_ADMIN') NOT NULL DEFAULT 'MEMBER',
PRIMARY KEY (`username`),
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类
public class User {
private String userName;
private String password;
private String roles;
public User() {
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
}
@Configuration
//@EnableWebSecurity
public class UserSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailService userDetailService; // 这个后面会提到
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll() // "/"不需要登录就能访问
.anyRequest().authenticated() // 其他链接都需要验证
.and()
.formLogin()
. loginPage("/user/login") // 指定登录页面
.usernameParameter("username") // html页面里input的name
.passwordParameter("password") // 如上
.defaultSuccessUrl("/") // 如果在登录页之前没输入页面,登陆成功后跳转的页面
.failureForwardUrl("/login/fail") // 验证失败后跳转的页面
.permitAll(); // 允许访问
http.csrf().disable(); // 这个地方一定要注意 不然post方法会返回403 被这个地方坑了很久
}
// 这个方法和下面的configureGlobal好像可以替换
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// 这里开始验证
// BCryptPasswordEncoder()可以替换成其他的加密方法
// 没添加passwordEncode方法的话会报没有加密的错误
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
}
数据存储到数据库之前记得用相同的加密方法对密码加密
注意这里不是一个包下的,class的是自己实现的,接口是org.springframework.security.core.userdetails.UserDetailsService
包下的
@Service
public class UserDetailService implements UserDetailsService {
@Autowired
private UserService userService; // UserService用的是mybatis,根据需要自己替换,执行数据库查找操作
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userService.findUserByName("uzck");
List<GrantedAuthority> list = new ArrayList<>();
// 权限管理这部分暂时还没了解,以后补上
getRoles(user, list);
org.springframework.security.core.userdetails.User authUser
= new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), list);
return authUser;
}
public void getRoles(User user, List<GrantedAuthority> list) {
for (String role : user.getRoles().split(",")) {
list.add(new SimpleGrantedAuthority("ROLE_" + role));
}
}
}
@Controller
public class UserController {
@RequestMapping("/")
public String index() {
return "index";
}
@RequestMapping("/hello")
public String hello() {
return "hello";
}
@RequestMapping("/user/login")
public ModelAndView login() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("login");
return modelAndView;
}
@RequestMapping("/login/fail")
public ModelAndView loginFail() {
return new ModelAndView("login-fail");
}
}
在resources目录下建立tempaltes文件夹,这个文件夹下的文件编译的时候默认打包。这里我用的是freemarker,其实用什么都行,直接html就可以
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<title>主页title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<style type="text/css">
body { padding: 40px; }
style>
head>
<body>
<h1>使用Spring Securityh1>
<p>点击 <a href="/hello">这里a>打招呼p>
body>
html>
<html>
<head>
head>
<body>
<h1>Hello Worldh1>
<form action="/logout">
<input type="submit" value="注销"/>
form>
body>
html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录title>
<style type="text/css">
body { padding: 20px; }
style>
head>
<body>
<form action="/user/login" method="post">
name: <input type="text" name="userName"><br/>
pass: <input type="password" name="pwd"><br/>
<input type="submit" value="登录">
form>
body>
html>
UserDetailService中的UserService
public interface UserService {
User findUserByName(String name);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User findUserByName(String name) {
return userDao.findUserByName(name);
}
}
@Mapper
public interface UserDao {
User findUserByName(@Param("userName") String name);
}
<mapper namespace="security.dao.UserDao">
<resultMap id="User" type="security.domain.User">
<result property="userName" column="username"/>
<result property="password" column="password"/>
<result property="roles" column="roles" />
resultMap>
<select id="findUserByName" parameterType="String" resultMap="User">
SELECT * from user WHERE username = #{userName}
select>
mapper>
#{userName}
是在UserDao的findUserByName方法里面用@Param("userName")
指定的
@SpringBootApplication
@MapperScan("security.dao")
public class SpringSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}
}
碰到的问题
上述两个问题都通过添加http.csrf().disable()
得到了解决,费了一天多功夫,终于折腾成功了。
参考资料: