springboot+mybatis+spring security
阿新 • • 發佈:2019-02-07
1、不囉嗦,直接上乾貨
首先新建專案springboot-security匯入需要的maven座標
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.0.RELEASE</version>
</parent>
<properties>
<start-class>com.us.example.Application</start-class>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<mybatis.version>3.2.7</mybatis.version>
<mybatis-spring.version >1.2.2</mybatis-spring.version>
</properties>
<dependencies>
<!--springboot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency >
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!--db-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--<!–打依賴包–>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2、自定義配置FilterSecurityInterceptor,而這個攔截器需要注入自己定義的SecruityMetadataService
3、由2可知新建MySecruityMetadataService,這個類需要實現FilterInvocationSecurityMetadataSource介面,在這個類中有主要的三部分:第一:載入所有的許可權列表;第二:取到當前訪問的路徑;第三:拿到的路徑為訪問路徑的許可權列表
@Service
public class MySecruityMetadataService implements
FilterInvocationSecurityMetadataSource {
@Autowired
private PermissionDao permissionDao;
private HashMap<String, Collection<ConfigAttribute>> map =null;
/**
* 載入資源,初始化資源變數
*/
public void loadResourceDefine(){
map = new HashMap<>();
Collection<ConfigAttribute> array;
ConfigAttribute cfg;
List<Permission> permissions = permissionDao.findAll();
for(Permission permission : permissions) {
array = new ArrayList<>();
cfg = new SecurityConfig(permission.getName());
array.add(cfg);
map.put(permission.getUrl(), array);
}
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if(map ==null) loadResourceDefine();
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
AntPathRequestMatcher matcher;
String resUrl;
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
resUrl = iter.next();
matcher = new AntPathRequestMatcher(resUrl);
if(matcher.matches(request)) {
return map.get(resUrl);
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
4、回退到第二步,注入自己的MySecruityMetadataService,執行過濾器(呼叫父類,父類中會執行)
@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//fi裡面有一個被攔截的url
//裡面呼叫MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的所有許可權
//再呼叫MyAccessDecisionManager的decide方法來校驗使用者的許可權是否足夠
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//執行下一個攔截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
5、配置自定義的MyUserDetailsService,查詢出此使用者的所有訪問許可權
@Service
public class CustomUserService implements UserDetailsService { //自定義UserDetailsService 介面
@Autowired
UserDao userDao;
@Autowired
PermissionDao permissionDao;
public UserDetails loadUserByUsername(String username) {
SysUser user = userDao.findByUserName(username);
if (user != null) {
List<Permission> permissions = permissionDao.findByAdminUserId(user.getId());
List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
for (Permission permission : permissions) {
if (permission != null && permission.getName()!=null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
grantedAuthorities.add(grantedAuthority);
}
}
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
} else {
throw new UsernameNotFoundException("admin: " + username + " do not exist!");
}
}
}
6、在自定義的MyAccessDecisionManager中判斷自己通過使用者名稱查詢出來的許可權列表中是否包含通過訪問路徑查詢出來的許可權。
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(null== configAttributes || configAttributes.size() <=0) {
return;
}
ConfigAttribute c;
String needRole;
for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
c = iter.next();
needRole = c.getAttribute();
for(GrantedAuthority ga : authentication.getAuthorities()) {
if(needRole.trim().equals(ga.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
7、頁面展示控制
<div sec:authorize="hasRole('ROLE_ADMIN')">
<!-- 使用者型別為ROLE_ADMIN 顯示 -->
<p class="bg-info">恭喜您,您有 ROLE_ADMIN 許可權 </p>
</div>
8、最後還需要一個配置類繼承WebSecurityConfigurerAdapter,實現對請求進行過濾
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //開啟方法級別的許可權註解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
@Autowired
UserDetailsService customUserService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService); //user Details Service驗證
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**").permitAll()
.anyRequest().authenticated() //任何請求,登入後可以訪問
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error")
.permitAll() //登入頁面使用者任意訪問
.and()
.logout().permitAll(); //登出行為任意訪問
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
}
8、開啟方法級別的註解許可權(第七配置類的@EnableGlobalMethodSecurity(prePostEnabled = true) //開啟方法級別的許可權註解)使用方式
@RequestMapping("/admin")
@ResponseBody
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public String hello(){
return "hello admin";
}
----------------ITteenager