赞
踩
一,用数据库实现权限管理要注意哪些环节?
1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息,
避免页面上显示用户昵称时需要查数据库
2,如果需要在页面上显示用户的登录信息,
需要自定义一个interceptor,
把用户的昵称等信息添加到 modelandview
3,普通用户的角色,即默认的权限,因为每个用户都具有,
就不要写入到数据表中,
避免数据量大时查询缓慢
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,项目地址:
https://github.com/liuhongdi/securitylogin
2,项目功能说明
演示了使用数据库实现的用户RBAC权限管理
三种页面:
无权限限制页面:任何人都可访问
需登录页面:修改密码等:登录才可以访问
有权限限制页面:必须授予相应的角色后才能访问
3,项目结构:如图:
三,配置文件说明
1,pom.xml
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-validation
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
mysql
mysql-connector-java
runtime
com.alibaba
fastjson
1.2.72
2,application.properties
#thymeleaf
spring.thymeleaf.cache=falsespring.thymeleaf.encoding=UTF-8spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/spring.thymeleaf.suffix=.html
#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/security?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=lhddemo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mybatis
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.mapper
#error
server.error.include-stacktrace=always
#log
logging.level.org.springframework.web=trace
3,数据库:
表结构:
CREATE TABLE`sys_user` (
`userId`int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`userName`varchar(100) NOT NULL DEFAULT '' COMMENT '用户名',
`password`varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
`nickName`varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '昵称',PRIMARY KEY(`userId`),UNIQUE KEY`userName` (`userName`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'
添加数据 :
INSERT INTO `sys_user` (`userId`, `userName`, `password`, `nickName`) VALUES(1, 'lhd', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '老刘'),
(2, 'admin', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '管理员'),
(3, 'merchant', '$2a$10$yGcOz3ekNI6Ya67tqQueS.raxyTOedGsv5jh2BwtRrI5/K9QEIPGq', '商户老张');
说明:3个密码都是111111,仅供演示使用,大家在生产环境中一定不要这样设置
CREATE TABLE`sys_user_role` (
`urId`int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`userId`int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`roleName`varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '角色id',PRIMARY KEY(`urId`),UNIQUE KEY`userId` (`userId`,`roleName`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色关联表'
插入数据:
INSERT INTO `sys_user_role` (`urId`, `userId`, `roleName`) VALUES(1, 2, 'ADMIN'),
(2, 3, 'MERCHANT');
四,java代码说明
1,WebSecurityConfig.java
@Configuration
@EnableWebSecuritypublic class WebSecurityConfig extendsWebSecurityConfigurerAdapter {private final static BCryptPasswordEncoder ENCODER = newBCryptPasswordEncoder();
@Resourceprivate UserLoginFailureHandler userLoginFailureHandler;//登录失败的处理类
@Resourceprivate UserLoginSuccessHandler userLoginSuccessHandler;//登录成功的处理类
@Resourceprivate UserLogoutSuccessHandler userLogoutSuccessHandler;//退出成功的处理类
@Resourceprivate UserAccessDeniedHandler userAccessDeniedHandler;//无权访问的处理类
@Resourceprivate SecUserDetailService secUserDetailService; //用户信息类,用来得到UserDetails//指定加密的方式,避免出现:There is no PasswordEncoder mapped for the id "null"
@Beanpublic PasswordEncoder passwordEncoder(){//密码加密类
return newBCryptPasswordEncoder();
}
@Overrideprotected void configure(HttpSecurity http) throwsException {//static
http.authorizeRequests()
.antMatchers("/css/**","/js/**","/img/**")//静态资源等不需要验证
.permitAll();//permitall
http.authorizeRequests()
.antMatchers("/home/**")//permitall
.permitAll();//login
http.formLogin()
.loginPage("/login/login")
.loginProcessingUrl("/login/logined")//发送Ajax请求的路径
.usernameParameter("username")//请求验证参数
.passwordParameter("password")//请求验证参数
.failureHandler(userLoginFailureHandler)//验证失败处理
.successHandler(userLoginSuccessHandler)//验证成功处理
.permitAll(); //登录页面用户任意访问//logout
http.logout()
.logoutUrl("/login/logout")
.logoutSuccessUrl("/login/logout")
.logoutSuccessHandler(userLogoutSuccessHandler)//登出处理
.deleteCookies("JSESSIONID")
.clearAuthentication(true)
.invalidateHttpSession(true)
.permitAll();//有角色的用户才能访问
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/merchant/**").hasAnyRole("MERCHANT","ADMIN");//其他任何请求,登录后可以访问
http.authorizeRequests().anyRequest().authenticated();//accessdenied
http.exceptionHandling().accessDeniedHandler(userAccessDeniedHandler);//无权限时的处理//user detail
http.userDetailsService(secUserDetailService);//rememberme//图形验证码//http.csrf().disable();
}
@Resourcepublic void configureGlobal(AuthenticationManagerBuilder auth) throwsException {
auth.userDetailsService(secUserDetailService).passwordEncoder(newPasswordEncoder() {
@OverridepublicString encode(CharSequence charSequence) {returnENCODER.encode(charSequence);
}//密码匹配,看输入的密码经过加密与数据库中存放的是否一样
@Overridepublic booleanmatches(CharSequence charSequence, String s) {returnENCODER.matches(charSequence,s);
}
});
}
}
2,SecUser.java
public class SecUser extendsUser {//用户id
private intuserid;//用户昵称
privateString nickname;public SecUser(String username, String password, Collection extends GrantedAuthority>authorities) {super(username, password, authorities);
}public SecUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection extends GrantedAuthority>authorities) {super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}publicString getNickname() {returnnickname;
}public voidsetNickname(String nickname) {this.nickname =nickname;
}public intgetUserid() {returnuserid;
}public void setUserid(intuserid) {this.userid =userid;
}
}
spring security中User类的子类,增加了用户id和昵称,
需要保存到session中的信息,在这里扩展
目的是避免在每个页面上显示用户信息需要查数据库
3,SecUserDetailService.java
/*** Created by liuhongdi on 2020/07/09.*/@Component("SecUserDetailService")public class SecUserDetailService implementsUserDetailsService{
@ResourceprivateSysUserService sysUserService;
@Overridepublic UserDetails loadUserByUsername(String s) throwsUsernameNotFoundException {//查库
SysUser oneUser = sysUserService.getOneUserByUsername(s);//数据库查询 看用户是否存在
String encodedPassword =oneUser.getPassword();
Collection collection = new ArrayList<>();//权限集合//用户权限:需要加 ROLE_
List roles =oneUser.getRoles();//System.out.println(roles);
for(String roleone : roles) {
GrantedAuthority grantedAuthority= new SimpleGrantedAuthority("ROLE_"+roleone);
collection.add(grantedAuthority);
}//增加用户的userid,nickname
SecUser user = newSecUser(s,encodedPassword,collection);
user.setUserid(oneUser.getUserId());
user.setNickname(oneUser.getNickName());returnuser;
}
}
4,UserAccessDeniedHandler.java
@Component("UserAccessDeniedHandler")public class UserAccessDeniedHandler implementsAccessDeniedHandler {
@Overridepublic voidhandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AccessDeniedException e)throwsIOException, ServletException {boolean isAjax =ServletUtil.isAjax();//System.out.println("isajax:"+isAjax);
if (isAjax == true) {
ServletUtil.printRestResult(RestResult.error(ResponseCode.ACCESS_DENIED));
}else{
ServletUtil.printString(ResponseCode.ACCESS_DENIED.getMsg());
}
}
}
5,UserLoginFailureHandler.java
@Component("UserLoginFailureHandler")public class UserLoginFailureHandler extendsSimpleUrlAuthenticationFailureHandler {
@Overridepublic voidonAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception)throwsIOException, ServletException {//System.out.println("UserLoginFailureHandler");
ServletUtil.printRestResult(RestResult.error(ResponseCode.LOGIN_FAIL));
}
}
6,UserLoginSuccessHandler.java
@Component("UserLoginSuccessHandler")public class UserLoginSuccessHandler extendsSimpleUrlAuthenticationSuccessHandler {
@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throwsIOException, ServletException {//System.out.println("UserLoginSuccessHandler");
ServletUtil.printRestResult(RestResult.success(0,"登录成功"));
}
}
7,UserLogoutSuccessHandler.java
@Component("UserLogoutSuccessHandler")public class UserLogoutSuccessHandler implementsLogoutSuccessHandler{
@Overridepublic void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throwsIOException, ServletException {
httpServletRequest.getSession().invalidate();
ServletUtil.printRestResult(RestResult.success(0,"退出成功"));
}
}
8,WebInterceptor.java
@Componentpublic class WebInterceptor extendsHandlerInterceptorAdapter {//如果view不为空,把登录信息传递给模板
@Overridepublic voidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {if (modelAndView != null) {
ModelMap modelMap=modelAndView.getModelMap();
SecUser currentUser=SessionUtil.getCurrentUser();if (currentUser != null) {
modelMap.addAttribute("is_login","1");
modelMap.addAttribute("login_username",currentUser.getNickname());
}else{
modelMap.addAttribute("is_login","0");
modelMap.addAttribute("login_username","");
}
}
}
}
负责把传递页面公共部分显示的数据到模板
9,login.html
登录页面body{padding-top:50px;
}.starter-template{padding:40px 15px;text-align:center;
}
首页Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。