欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Sprng boot + Spring security整合

程序员文章站 2022-04-19 22:31:53
...

Sprng boot + Spring security整合

本文主要介绍Sprng boot + Spring security实战整合,不涉及概念性内容,开发环境idea+jdk1.8+maven+Sprng boot 2.1.4

效果展示

输入任意路径被拦截并跳转至登录页面
Sprng boot + Spring security整合
输入错误用户名或密码
Sprng boot + Spring security整合
登录成功
Sprng boot + Spring security整合
访问home页面
Sprng boot + Spring security整合

导包

<?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.1.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springsecurity</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</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-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.20</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-cas</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <!-- security taglibs -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.1</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version><!--$NO-MVN-MAN-VER$-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

编写项目配置

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///test
spring.datasource.password=root
spring.datasource.username=root
#数据库连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#sql的xml文件配置
mybatis.mapper-locations=classpath:mapper/*.xml

实体类

User类

@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails{
    private Integer id;

    private String username;

    private String password;

    private Boolean enabled;

    private Boolean locked;

    private List<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public Boolean getLocked() {
        return locked;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return !locked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", enabled=" + enabled +
                ", locked=" + locked +
                ", roles=" + roles +
                '}';
    }
}

Role 类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    private Integer id;

    private String name;

    private String nameZh;

}

dao层

mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springsecurity.demo.dao.MyUserDao">
    <select id="loadUserByUsername" resultType="com.springsecurity.demo.pojo.User" parameterType="string">
        select * from adminuser where username = #{username}
    </select>

   <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springsecurity.demo.dao.MyUserDao">
    <select id="loadUserByUsername" resultType="com.springsecurity.demo.pojo.User" parameterType="string">
        select * from adminuser where username = #{username}
    </select>

    <select id="getUserRolesByUserId" resultType="com.springsecurity.demo.pojo.Role">
        select * from role r left join  user_role ur on  r.id=ur.rid  and ur.uid=#{userId}
    </select>
</mapper>

MyUserDao

@Mapper
public interface MyUserDao {
    /**
     * 通过用户名获取用户信息
     *
     * @param username 用户名
     * @return User 用户信息
     */
    User loadUserByUsername(String username);

    /**
     * 通过用户id获取用户角色集合
     *
     * @param userId 用户id
     * @return List<Role> 角色集合
     */
    List<Role> getUserRolesByUserId(Integer userId);
}

下面进入最重要的一部分(划重点)

因为是spring boot环境所以我们首先要写好Spring security的Java配置
Spring security config配置

@SpringBootConfiguration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
    @Autowired
    CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
    @Autowired
    MyBCryptPasswordEncoder myBCryptPasswordEncoder;
    @Autowired
    UserService userService;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().
        //拦截除‘/’外的所有请求
                    antMatchers("/").
                    permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
        //指定登录路径
                .formLogin()
                    .loginPage("/login")
                    .permitAll()
 		//登录成功
                .successHandler(customAuthenticationSuccessHandler)
        //登录失败
                .failureHandler(customAuthenticationFailureHandler)
                .and();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //用户名密码验证
       auth.userDetailsService(userService).passwordEncoder(myBCryptPasswordEncoder);
    }
}

登录成功处理类

@Slf4j
@Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException {
        //得到登录的用户信息
        User principal = (User) auth.getPrincipal();
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        response.setStatus(200);
        Map<String, Object> map = new HashMap<>(16);
        //密码置空
        principal.setPassword("");
        map.put("status",200);
        map.put("msg",principal);
        ObjectMapper om = new ObjectMapper();
        out.write(om.writeValueAsString(map));
        out.flush();
        out.close();
    }

}

登录失败处理类

@Slf4j
@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        response.setStatus(401);
        Map<String, Object> map = new HashMap<>(16);
        map.put("status", 401);
        if (e instanceof LockedException) {
            map.put("msg", "账户被锁定,登录失败!");
        } else if (e instanceof BadCredentialsException) {
            map.put("msg", "账户名或密码输入错误,登录失败!");
        } else if (e instanceof DisabledException) {
            map.put("msg", "账户被禁用,登录失败!");
        } else if (e instanceof CredentialsExpiredException) {
            map.put("msg", "密码已过期,登录失败!");
        } else {
            map.put("msg", "登录失败!");
        }
        ObjectMapper om = new ObjectMapper();
        out.write(om.writeValueAsString(map));
        out.flush();
        out.close();
    }
}

用户名验证

@Service
public class UserService implements UserDetailsService {
    @Resource
    MyUserDao userDao;
    @Override
    public UserDetails loadUserByUsername(String s){
        User user = userDao.loadUserByUsername(s);
        if (user == null) {
            throw new UsernameNotFoundException("该用户不存在!");
        }
        List<Role> roles = new ArrayList<>();
        roles.add(new Role(1,"user","user"));
        user.setRoles(roles);
        return user;
    }
}

密码验证

@Slf4j
@Component
public class MyBCryptPasswordEncoder extends BCryptPasswordEncoder {
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword){
        //密码验证
        //encodedPassword原始密码
        //rawPassword输入的密码
        if (encodedPassword.equals(rawPassword)){
            return true;
        }
        return false;
    }
}

最后加上一个controller和页面来感受一下我们的成果
controller

@Controller
public class TextController {
    @RequestMapping("/")
    public String toHome(){
        return "/home";
    }
    @RequestMapping("/home")
    public String toHomes(){
        return "/home";
    }
    @RequestMapping("/login")
    public String toLogin(){
        return "/login";
    }
}

登录页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    Invalid username and password.
</div>
<div th:if="${param.logout}">
    You have been logged out.
</div>
<form th:action="@{/login}" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

home页面

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="UTF-8">
    <title>Spring Security Example</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
    <input type="submit" value="Sign Out"/>
</form>
</body>
</html>