Spring Boot整合Spring Security验证用户登录

配置

代码地址: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>

application.yml

这里具体的配置还是按照自己的工程来修改

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;
    }
}

Spring Security配置

@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);
    }

}

数据存储到数据库之前记得用相同的加密方法对密码加密

UserDetailService

注意这里不是一个包下的,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));
        }
    }
}

UserController

@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");
   }


}

templates

在resources目录下建立tempaltes文件夹,这个文件夹下的文件编译的时候默认打包。这里我用的是freemarker,其实用什么都行,直接html就可以

index.ftl


<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>

hello.ftl

<html>
    <head>
    head>
    <body>
        <h1>Hello Worldh1>
        <form action="/logout">
            <input type="submit" value="注销"/>
        form>
    body>
html>

login.ftl


<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>

Mybatis部分

UserDetailService中的UserService

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);
    }
}

UserDao

@Mapper
public interface UserDao {
    User findUserByName(@Param("userName") String name);
}

userMapper.xml



<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);
    }
}

总结

碰到的问题

  1. HTTP 403
  2. 登录页点击登录还是跳转登录页,循环跳转,没有进到验证的步骤

上述两个问题都通过添加http.csrf().disable()得到了解决,费了一天多功夫,终于折腾成功了。
参考资料:

  1. Spring MVC Post请求返回403错误,Get请求却正常?
  2. 初步理解Spring Security并实践
  3. spring boot 整合 spring security 登录认证

你可能感兴趣的:(spring,Java,Web)