java url权限控制_springboot整合security实现基于url的权限控制

本文详细介绍了如何使用Spring Security实现基于URL的权限控制。内容包括:权限控制的两个过程——认证和授权,Spring Security的工作原理,以及整合Spring Security的步骤,如引入依赖、建立权限表、配置WebSecurity等。示例代码展示了如何自定义过滤器、权限源、决策管理器等关键组件,以实现用户登录、权限判断和异常处理。
摘要由CSDN通过智能技术生成

权限控制基本上是任何一个web项目都要有的,为此spring为我们提供security模块来实现权限控制,网上找了很多资料,但是提供的demo代码都不能完全满足我的需求,因此自己整理了一版。

在上代码之前,大家需要理解两个过程:认证和授权

用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。

访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

整合步骤如下:

1、引入依赖和添加mybatis generator插件

4.0.0

powerx.io

springboot-security

0.0.1-SNAPSHOT

jar

springboot-security

Demo project for Spring Boot

org.springframework.boot

spring-boot-starter-parent

2.0.5.RELEASE

UTF-8

UTF-8

1.8

org.springframework.boot

spring-boot-starter-web

org.mybatis.spring.boot

mybatis-spring-boot-starter

1.3.2

mysql

mysql-connector-java

runtime

com.github.pagehelper

pagehelper-spring-boot-starter

1.2.5

com.alibaba

druid-spring-boot-starter

1.1.9

org.springframework.boot

spring-boot-starter-security

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-maven-plugin

org.mybatis.generator

mybatis-generator-maven-plugin

1.3.2

src/main/resources/generator/generatorConfig.xml

true

true

2、建立对应的表,标准的基于角色权限控制的五张表,建表语句我也放到代码中了。

c75479d5ea4902187d6798950731c6b1.png

3、利用逆向工程生成对应的model、mapper和映射文件等

4、spring security配置,关键位置我都加了注释

WebSecurityConfig.java

packagecom.example.demo.config;importjava.io.IOException;importjava.io.PrintWriter;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.authentication.BadCredentialsException;importorg.springframework.security.config.annotation.ObjectPostProcessor;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.builders.WebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.access.intercept.FilterSecurityInterceptor;importorg.springframework.security.web.authentication.AuthenticationFailureHandler;importorg.springframework.security.web.authentication.AuthenticationSuccessHandler;importcom.example.demo.service.UserService;

@Configurationpublic class WebSecurityConfig extendsWebSecurityConfigurerAdapter {

@Autowired

UserService userService;

@Autowired

MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;

@Autowired

MyAccessDecisionManager myAccessDecisionManager;

@Autowired

AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;/*** 自定义的加密算法

*@return

*/@BeanpublicPasswordEncoder myPasswordEncoder() {return newMyPasswordEncoder();

}

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throwsException {

auth.userDetailsService(userService).passwordEncoder(myPasswordEncoder());

}

@Overridepublic void configure(WebSecurity web) throwsException {

web.ignoring().antMatchers("/index.html", "/static/**","/loginPage","/register");

}

@Overrideprotected void configure(HttpSecurity http) throwsException {

http.authorizeRequests()

.withObjectPostProcessor(new ObjectPostProcessor() {

@Overridepublic O postProcess(O o) {

o.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);

o.setAccessDecisionManager(myAccessDecisionManager);returno;

}

}).and().formLogin().loginPage("/loginPage").loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password").permitAll().failureHandler(newAuthenticationFailureHandler() {

@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throwsIOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out=httpServletResponse.getWriter();

StringBuffer sb= newStringBuffer();

sb.append("{\"status\":\"error\",\"msg\":\"");if (e instanceof UsernameNotFoundException || e instanceofBadCredentialsException) {

sb.append("用户名或密码输入错误,登录失败!");

}else{

sb.append("登录失败!");

}

sb.append("\"}");

out.write(sb.toString());

out.flush();

out.close();

}

}).successHandler(newAuthenticationSuccessHandler() {

@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throwsIOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out=httpServletResponse.getWriter();

String s= "{\"status\":\"success\",\"msg\":\"登陆成功\"}";

out.write(s);

out.flush();

out.close();

}

}).and().logout().permitAll().and().csrf().disable().exceptionHandling().accessDeniedHandler(authenticationAccessDeniedHandler);

}

}

MyFilterInvocationSecurityMetadataSource.java

packagecom.example.demo.config;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.access.ConfigAttribute;importorg.springframework.security.access.SecurityConfig;importorg.springframework.security.web.FilterInvocation;importorg.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;importorg.springframework.security.web.util.matcher.AntPathRequestMatcher;importorg.springframework.stereotype.Service;importcom.example.demo.dao.PermissionMapper;importcom.example.demo.model.Permission;importjavax.servlet.http.HttpServletRequest;import java.util.*;importjava.util.Map.Entry;

@Servicepublic class MyFilterInvocationSecurityMetadataSource implementsFilterInvocationSecurityMetadataSource {

@AutowiredprivatePermissionMapper permissionMapper;private HashMap> map = null;/*** 加载权限表中所有权限*/

public voidloadResourceDefine() {

map= new HashMap>();

List permissions =permissionMapper.findAll();for(Permission permission : permissions) {

ConfigAttribute cfg= newSecurityConfig(permission.getPermissionname());

List list = new ArrayList<>();

list.add(cfg);

map.put(permission.getUrl(), list);

}

}/*** 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法, 用来判定用户

* 是否有此权限。如果不在权限表中则放行。*/@Overridepublic Collection getAttributes(Object object) throwsIllegalArgumentException {if (map == null) {

loadResourceDefine();

}//object 中包含用户请求的request的信息

HttpServletRequest request =((FilterInvocation) object).getHttpRequest();for (Entry>entry : map.entrySet()) {

String url=entry.getKey();if (newAntPathRequestMatcher(url).matches(request)) {returnmap.get(url);

}

}return null;

}

@Overridepublic CollectiongetAllConfigAttributes() {return null;

}

@Overridepublic boolean supports(Class>clazz) {return true;

}

}

MyAccessDecisionManager.java

packagecom.example.demo.config;importorg.springframework.security.access.AccessDecisionManager;importorg.springframework.security.access.AccessDeniedException;importorg.springframework.security.access.ConfigAttribute;importorg.springframework.security.authentication.InsufficientAuthenticationException;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.stereotype.Service;importjava.util.Collection;importjava.util.Iterator;

@Servicepublic class MyAccessDecisionManager implementsAccessDecisionManager {/*** decide 方法是判定是否拥有权限的决策方法,authentication是CustomUserService

* 中循环添加到 GrantedAuthority 对象中的权限信息集合,object 包含客户端发起的请求的requset信息,

* 可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

* configAttributes为MyFilterInvocationSecurityMetadataSource的getAttributes(Object object)

* 这个方法返回的结果.

**/@Overridepublic void decide(Authentication authentication, Object object, Collection configAttributes) throwsAccessDeniedException, InsufficientAuthenticationException {if(null== configAttributes || configAttributes.size() <=0) {return;

}

ConfigAttribute c;

String needRole;for(Iterator iter =configAttributes.iterator(); iter.hasNext(); ) {

c=iter.next();

needRole=c.getAttribute();for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合

if(needRole.trim().equals(ga.getAuthority())) {return;

}

}

}throw new AccessDeniedException("no right");

}

@Overridepublic booleansupports(ConfigAttribute attribute) {return true;

}

@Overridepublic boolean supports(Class>clazz) {return true;

}

}

AuthenticationAccessDeniedHandler.java

packagecom.example.demo.config;importorg.springframework.security.access.AccessDeniedException;importorg.springframework.security.web.access.AccessDeniedHandler;importorg.springframework.stereotype.Component;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;

@Componentpublic class AuthenticationAccessDeniedHandler implementsAccessDeniedHandler {

@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse resp, AccessDeniedException e) throwsIOException, ServletException {

resp.setStatus(HttpServletResponse.SC_FORBIDDEN);

resp.setContentType("application/json;charset=UTF-8");

PrintWriter out=resp.getWriter();

out.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员!\"}");

out.flush();

out.close();

}

}

MyPasswordEncoder.java

packagecom.example.demo.config;importorg.springframework.security.crypto.password.PasswordEncoder;public class MyPasswordEncoder implementsPasswordEncoder {

@OverridepublicString encode(CharSequence charSequence) {returncharSequence.toString();

}

@Overridepublic booleanmatches(CharSequence charSequence, String s) {returns.equals(charSequence.toString());

}

}

UserServiceImpl.java

packagecom.example.demo.service.impl;importjava.util.ArrayList;importjava.util.List;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importcom.example.demo.dao.PermissionMapper;importcom.example.demo.dao.RoleMapper;importcom.example.demo.dao.UserMapper;importcom.example.demo.model.Permission;importcom.example.demo.model.User;importcom.example.demo.service.UserService;

@Servicepublic class UserServiceImpl implementsUserService {

@AutowiredprivatePermissionMapper permissionMapper;

@AutowiredprivateRoleMapper roleMapper;

@AutowiredprivateUserMapper userMapper;

@AutowiredprivatePasswordEncoder passwordEncoder;

@Overridepublic UserDetails loadUserByUsername(String username) throwsUsernameNotFoundException {

User user=userMapper.selectByUsername(username);if (user != null) {

List permissions =permissionMapper.findByUserId(user.getId());

List grantedAuthorities = new ArrayList <>();for(Permission permission : permissions) {if (permission != null && permission.getPermissionname()!=null) {

GrantedAuthority grantedAuthority= newSimpleGrantedAuthority(permission.getPermissionname());

grantedAuthorities.add(grantedAuthority);

}

}return neworg.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);

}else{throw new UsernameNotFoundException("username: " + username + " do not exist!");

}

}

@Transactional

@Overridepublic voiduserRegister(String username, String password) {

User user= newUser();

user.setUsername(passwordEncoder.encode(username));

user.setPassword(password);

userMapper.insert(user);

User rtnUser=userMapper.selectByUsername(username);//注册成功默认给用户的角色是user

roleMapper.insertUserRole(rtnUser.getId(), 2);

}

}

至此,整合基本完毕,其它控制层的代码和mapper层的代码不再贴出,需要注意的是注册用户的时候我们要用自定义的加密工具对密码进行加密(当然在demo中我什么也没做),其它的一些功能比如给用户加角色、给角色加权限等的增删改查,大家可以根据需要自行添加,另外在permissionMapper.findByUserId(user.getId())这里我写了一个五张表的关联查询,可以根据userid可以查出用户所有对应的权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值