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;