SpringBoot 整合Spring Security

SpringBoot 整合SpringSecurity

导入依赖

<?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.0.4.RELEASE</version>
    </parent>

    <groupId>cn.blogsx</groupId>
    <artifactId>springboot_spring_security</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- web功能起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Spring Security依赖包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--thymeleaf依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--  mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!-- druid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
        <!-- mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

配置数据库

配置连接数据库的参数,方便做基于数据库的RABC动态权限管理

server.port=8080

# 数据库连接相关配置
spring.datasource.url=jdbc:mysql:///springsecurity?characterEncoding=utf8&useSSL=true
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

# MyBatis注解形式扫描实体类路径
mybatis.type-aliases-package=cn.blogsx.entity

# MyBatis XML形式配置文件路径
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

# 测试基于配置的springSecurity 用户名和密码
#spring.security.user.name=alex
#spring.security.user.password=123456
#spring.security.user.roles=admin

创建SpringSecurity 配置类

@Configuration //Spring Security 拦截和授权管理配置类
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(10); //配置加密参数
    }
    @Bean //该bean可结合.sessionManagement().maximumSessions(1)配置完成踢出登陆功能
    HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasRole("admin")
                .antMatchers("/user/**")
                .hasRole("user")
                .antMatchers("/db/**")
                .hasRole("dba")
                .anyRequest()
                .authenticated()
                .and()
                .rememberMe()//实现记住我功能
                .key("sxblog")//指定remember-me session加密key(即使重启服务器也可保持用户在线)
                .and()
                .formLogin()//表单登陆
                .loginPage("/login").permitAll() //用户未登陆,配置登陆页面接口或者json提示
                .loginProcessingUrl("/login").permitAll()//表单提交接口,默认也是login接口
                .usernameParameter("username")//指定登陆页面或前后端分离下发送请求的字段
                .passwordParameter("password")//指定登陆页面或前后端分离下发送请求的字段
                .successHandler(new AuthenticationSuccessHandler() { //定义认证成功后返回json信息
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        Object principle = authentication.getPrincipal();
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        httpServletResponse.setStatus(200);
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",200);
                        map.put("msg","登陆成功!");
                        ObjectMapper om = new ObjectMapper();
                        out.write(om.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() { //登陆认证失败返回json响应信息
            @Override
            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                httpServletResponse.setStatus(401);
                Map<String,Object> map = new HashMap<>();
                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 AccountExpiredException) {
                    map.put("msg","账户已过期,登陆失败!");
                }else if (e instanceof CredentialsExpiredException) {
                    map.put("msg","密码已过期,登陆失败!");
                } else {
                    e.printStackTrace();
                    map.put("msg","登陆失败!");
                }
                ObjectMapper om = new ObjectMapper();
                out.write(om.writeValueAsString(map));
                out.flush();
                out.close();
            }
        })
                .permitAll()//允许所有人访问登陆接口
                .and()
                .logout()//配置注销登陆
                .logoutUrl("/logout")//配置登出接口
                .clearAuthentication(true)//清除认证信息
                .invalidateHttpSession(true)//使Session失效
                .addLogoutHandler(new LogoutHandler() {
                    @Override
                    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
                    }
                })
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        httpServletResponse.setStatus(200);
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",200);
                        map.put("msg","注销成功!");
                        ObjectMapper om = new ObjectMapper();
                        out.write(om.writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                .csrf()
                .disable()
                //关闭csrf安全保护
                .exceptionHandling() //未登陆状态返回json提示
                .authenticationEntryPoint((req, resp, authException) -> {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            out.write("尚未登录,请先登录");
                            out.flush();
                            out.close();
                        }
                )
                .and()
                .sessionManagement()
                .maximumSessions(1)//只允许一台设备登陆(新的登录踢掉旧的登录)
                .maxSessionsPreventsLogin(true);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//            auth.inMemoryAuthentication()
//                    .withUser("root").password("$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu").roles("ADMIN","DBA")
//                    .and()
//                    .withUser("admin").password("$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu").roles("ADMIN","USER")
//                    .and()
//                    .withUser("alex").password("$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu").roles("USER");
        auth.userDetailsService(userService);
    }
}

创建相关接口做测试

@RestController
public class HelloController {

    @RequestMapping("/admin/hello")
    public String adminHello() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
        System.out.println("IP地址为:"+details.getRemoteAddress());
        return "hello admin1";
    }
    @RequestMapping("/user/hello")
    public String userHello() {
        return "hello user!";
    }
    @RequestMapping("/db/hello")
    public String dbHello() {
        return "hello dba!";
    }
    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }
}

@Controller
public class MainController {
    @RequestMapping("/userLogin")
//    @ResponseBody
    public Map<String,Object> loginPage() {
        HashMap<String,Object> map = new HashMap<>();
        map.put("msg","用户未登录,请登陆!");
        return map;
    }

    @RequestMapping("/logout_res")
    @ResponseBody
    public Map<String,Object> logOut() {
        System.out.println("logout_res!");
        HashMap<String,Object> map = new HashMap<>();
        map.put("msg","用户已登出");
        return map;
    }
    @RequestMapping("/login")
    public String login() {
        return "login";
    }
}

基于数据库做动态权限认证

实体类

public class Role {
    private Integer id;
    private String name;
    private String nameZh;
//省略getter和setter
}
public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;

    public User() {
    }

    public User(Integer id, String username, String password, Boolean enabled, Boolean locked, List<Role> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.locked = locked;
        this.roles = roles;
    }

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

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

    public Integer getId() {
        return id;
    }

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

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

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

创建数据库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL,
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  `nameZh` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL,
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
  `enabled` tinyint(1) NULL DEFAULT NULL,
  `locked` tinyint(1) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', '$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu', 1, 0);
INSERT INTO `user` VALUES (2, 'admin', '$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu', 1, 0);
INSERT INTO `user` VALUES (3, 'alex', '$2a$10$8XXMNg8WQ8YlSIGGcgnaw./zrf2k6klkqXs0ezawj43VN7uh/m8Wu', 1, 0);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(11) NOT NULL,
  `uid` int(11) NULL DEFAULT NULL,
  `rid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Fixed;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);

SET FOREIGN_KEY_CHECKS = 1;

源码地址

https://gitee.com/sixudev/SpringBootStudy

posted @ 2020-11-03 09:12  sxblog  阅读(162)  评论(0编辑  收藏  举报