spring-security在spring-boot下的使用

留个笔记,以后自己可以看看,spring-security 的配置demo,会陆续补充

idea

maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

引入后,写个RestController

@RestController
public class HelloController {
    @RequestMapping("/hello")
    String home() {
        return "Hello ,spring security!";
    }
}

启动Spring-boot,发现控制台有个东西

输入:http://localhost:8080/hello,发现跳转到一个登录页:http://localhost:8080/login

账号是:user,密码是控制台上的security password

跳转过了,神奇。

现在没有任何的配置,只要输入账号密码用了,现在干下配置



@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    //设置密码加密策略,这里使用明文密码,即无加密策略
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())//声明密码策略
                .withUser("spring") .password("123456").roles("USER")//内存创建用户、权限
                .and()
                .withUser("admin").password("123456").roles("ADMIN", "USER");

    }




      @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()// 定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers("/product/**").hasRole("USER")//部分请求需要某个角色才能访问
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/hello*").hasRole("ADMIN")
                .anyRequest().authenticated()// 任何请求,登录后可以访问
               .and().httpBasic()//http支持基础认证
              . and().formLogin()// 定义当需要用户登录时候,转到的登录页面。默认是spring-security自带的
               .successForwardUrl("/hello")//登录成功默认跳转的地址
        ;
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/vercode");//忽略某些地址
    }
}

controller写法:

@RestController
public class HelloController {
    @RequestMapping("/hello")
    String home() {
        return "Hello ,spring security!";
    }
    @RequestMapping("/vercode")
    String vercode() {
        return "Hello ,vercode security!";
    }
    @RequestMapping("/product/info")
    public String productInfo(){
        return " some product info ";
    }
    @RequestMapping("/admin/home")
    public String adminInfo(){
        return " admin home page ";
    }


}

 

启动项目:

 http://localhost:8080/vercode,直接运行成功不需要登录

http://localhost:8080/ 跳出登录页面,输入密码后,就可以运行成功了(spring账户登录)

http://localhost:8080/hello 出现403,http://localhost:8080/product/info成功访问

下面试试,走数据库获取账号密码

数据库随便创建个用户表

创建用户实体,实现框架用户对象,这里只简单对账号密码进行测试学习


public class AisinoUser implements UserDetails {
    private  String username;
    private String userpassword;

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserpassword() {
        return userpassword;
    }

    public void setUserpassword(String userpassword) {
        this.userpassword = userpassword;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return this.userpassword;
    }

    @Override
    public String getUsername() {
        return this.username;
    }
    //账户是否过期
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    //账户是否被锁
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    //密码是否过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    //账户是否可用
    @Override
    public boolean isEnabled() {
        return true;
    }
}

框架的用户有了,再实现框架那块判定用户的代码,实现UserDetailsService,重写loadUserByUsername方法

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    UserDetailDao dao;
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        AisinoUser userByUsername = dao.getUserByUsername(userName);
        if(userByUsername==null){
            throw  new UsernameNotFoundException("用户未找到");
        }
        return userByUsername;
    }
}

自己写个查找用户的dao

@Mapper
public interface UserDetailDao {
    AisinoUser getUserByUsername(String username);
}
<mapper namespace="com.example.demo.hello.dao.UserDetailDao">

    <select id="getUserByUsername"  resultType="com.example.demo.hello.bo.AisinoUser">
        select
        *
        from aisinouser
        where username=#{username};
    </select>
</mapper>

配置那边改改,当时用的内存载入密码,这次通过数据库查询密码


@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailServiceImpl service;
    //设置密码加密策略,这里使用明文密码,即无加密策略
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

//        auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())//声明密码策略
//                .withUser("spring") .password("123456").roles("USER")//内存创建用户、权限
//                .and()
//                .withUser("admin").password("123456").roles("ADMIN", "USER");
       auth.userDetailsService(service);
    }




        @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()// 定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers("/product/**").hasRole("USER")//部分请求需要某个角色才能访问
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/hello*").authenticated()
                .anyRequest().authenticated()// 任何请求,登录后可以访问
               .and().httpBasic()//http支持基础认证
              . and().formLogin()// 定义当需要用户登录时候,转到的登录页面。默认是spring-security自带的
              // .successForwardUrl("/hello")//登录成功默认跳转的地址
        ;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/vercode");//忽略某些地址
    }
}

输入localhost:8080 跳转到登录页面,账号密码输入1,出现404,说明成功

输入localhost:8080/hello,跳转正常 

输入localhost:8080/product/info,出现type=Forbidden, status=403正常,说明没有赋予角色

后续参考:https://blog.csdn.net/zhaoxichen_10/article/details/88713799

发现好文:https://www.cnblogs.com/zhengqing/p/11704229.html?utm_source=tuicool&utm_medium=referral

赋予用户角色:

数据库写好部分角色,这边角色大小写要注意,默认比较角色的时候用equals

mybatis写好:

  <select id="getUserRoles"  resultType="com.example.demo.hello.bo.UserRoles">
        select
        *
        from user_role
        where username=#{username};
    </select>

在loadUserByUsername那个认证方法里面,加上角色信息,方法完整的写法:

 @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        AisinoUser userByUsername = dao.getUserByUsername(userName);
        if(userByUsername==null){
            throw  new UsernameNotFoundException("用户未找到");
        }
        List<UserRoles> userRoles = dao.getUserRoles(userName);
        if (userRoles!=null && userRoles.size()>0) {
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>(userRoles.size());
            for (UserRoles role : userRoles) {
                //框架角色以ROLE_开头的,所以我们先加上
                    grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRole()));
            }
            userByUsername.setAuthorities(grantedAuthorities);
        }
        return userByUsername;
    }

配置那边,还是先用内存模式配置角色

  http
                .authorizeRequests()
                .antMatchers("/product/**").hasRole("user")//部分请求需要某个角色才能访问,角色大小写要注意
                .antMatchers("/admin/**").hasRole("admin1")
                .antMatchers("/hello*").authenticated()
                .anyRequest().authenticated()// 任何请求,登录后可以访问
               .and().httpBasic()//http支持基础认证
              . and().formLogin()// 定义当需要用户登录时候,转到的登录页面。默认是spring-security自带的
            
        ;

启动服务试试,登录后,走/hello

走product/info

走admin/home

正常

整完这个,下面不满足地址、角色权限是固定值写在配置文件里面,那么就动态配置咯

后续参考:https://blog.csdn.net/zhaoxichen_10/article/details/88713799

发现好文:https://www.cnblogs.com/zhengqing/p/11704229.html?utm_source=tuicool&utm_medium=referral

核心:重写实现FilterInvocationSecurityMetadataSource,重写getAttributes()方法 获取访问该url所需要的角色权限信息;重写实现AccessDecisionManager,重写decide()方法 对访问url进行权限认证处理,上代码:

package com.example.demo.hello.config;

import com.example.demo.hello.bo.MenuRoles;
import com.example.demo.hello.dao.UserDetailDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Component
public class MyFilterInvocationSecurityMetadataSource
       implements FilterInvocationSecurityMetadataSource
{
    @Autowired
    UserDetailDao dao;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     动态加载此链接需要什么权限,权限可以写在数据库,但不能决策,只能查询,查询完了,给决策使用
     */
    public Collection<ConfigAttribute> getAttributes(Object object) {
        HttpServletRequest request = ((FilterInvocation)object).getRequest();
        String requestUrl =request.getRequestURI();
        List<MenuRoles> allMenu = dao.getMenuRoles();
        List<ConfigAttribute> attributes = new ArrayList();
        for (MenuRoles menu : allMenu) {
            if (antPathMatcher.match(menu.getMenu_url(), requestUrl)) {
                SecurityConfig securityConfig = new SecurityConfig("ROLE_"+menu.getMenu_role().trim());
                attributes.add(securityConfig);
            }
        }
        if(attributes.size()==0){
            return SecurityConfig.createList("ROLE_LOGIN");
        }
      return attributes;
    }
    /**
     * @Author: Galen
     * @Description: 此处方法如果做了实现,返回了定义的权限资源列表,
     * Spring Security会在启动时校验每个ConfigAttribute是否配置正确,
     * 如果不需要校验,这里实现方法,方法体直接返回null即可。
     * @Date: 2019/3/27-17:12
     * @Param: []
     * @return: java.util.Collection<org.springframework.security.access.ConfigAttribute>
     **/
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
package com.example.demo.hello.config;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Iterator;

/**
 * 决策器
 */
/**
 * @Author: Galen
 * @Date: 2019/3/27-16:59
 * @Description: Decision决定的意思。
 * 有了权限资源(MyFilterInvocationSecurityMetadataSource),知道了当前访问的url需要的具体权限,接下来就是决策当前的访问是否能通过权限验证了
 * MyAccessDecisionManager 自定义权限决策管理器
 **/
@Component
public class MyAccessDecisionManager  implements AccessDecisionManager {


    /**
     * @Author: Galen
     * @Description: 取当前用户的权限与这次请求的这个url需要的权限作对比,决定是否放行
     * auth 包含了当前的用户信息,包括拥有的权限,即之前UserDetailsService登录时候存储的用户对象
     * object 就是FilterInvocation对象,可以得到request等web资源。
     * configAttributes 是本次访问需要的权限。即上一步的 MyFilterInvocationSecurityMetadataSource 中查询核对得到的权限列表
     * @Date: 2019/3/27-17:18
     * @Param: [auth, object, cas]
     * @return: void
     **/
    @Override
    public void decide(Authentication auth, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();

        while (iterator.hasNext()) {
            if (auth == null) {
                throw new AccessDeniedException("当前访问没有权限");
            }
            ConfigAttribute ca = iterator.next();
            //当前请求需要的权限,在自定义MyFilterInvocationSecurityMetadataSource .getAttributes 中返回的
            String needRole = ca.getAttribute();
            if ("ROLE_LOGIN".equals(needRole)) {//默认此页面不需要权限登录,判定用户是否登录,没登录走登录,否则,房型
                if (auth instanceof AnonymousAuthenticationToken) {
                    throw new BadCredentialsException("未登录");
                } else
                    return;
            }
//
            SecurityContext context = SecurityContextHolder.getContext();
                Authentication authentication = context.getAuthentication();
               String username = authentication.getName();
                Object principal = authentication.getPrincipal();
           //   Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); 
            //当前用户所具有的权限
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

 

package com.example.demo.hello.dao;


import com.example.demo.hello.bo.AisinoUser;
import com.example.demo.hello.bo.MenuRoles;
import com.example.demo.hello.bo.UserRoles;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserDetailDao {
    AisinoUser getUserByUsername(String username);

    /**
     * 获取用户角色
     * @param username
     * @return
     */
  List< UserRoles> getUserRoles(String username);

    /**
     * 获取菜单需要的权限
     * @return
     */
    List<MenuRoles> getMenuRoles();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.hello.dao.UserDetailDao">

    <select id="getUserByUsername"  resultType="com.example.demo.hello.bo.AisinoUser">
        select
        *
        from aisinouser
        where username=#{username};
    </select>

    <select id="getUserRoles"  resultType="com.example.demo.hello.bo.UserRoles">
        select
        *
        from user_role
        where username=#{username};
    </select>
    <select id="getMenuRoles"  resultType="com.example.demo.hello.bo.MenuRoles">
        select
        *
        from menu_role
    </select>
</mapper>

 

 

 

security核心配置

package com.example.demo.hello.config;

import com.example.demo.hello.service.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailServiceImpl service;
    //设置密码加密策略,这里使用明文密码,即无加密策略

    @Autowired
    private MyFilterInvocationSecurityMetadataSource filterMetadataSource; //权限过滤器(当前url所需要的访问权限)
    @Autowired
    private MyAccessDecisionManager myAccessDecisionManager;//权限决策器
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {

//        auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())//声明密码策略
//                .withUser("spring") .password("123456").roles("USER")//内存创建用户、权限
//                .and()
//                .withUser("admin").password("123456").roles("ADMIN", "USER");
       auth.userDetailsService(service);
    }
//    @Bean
//    @Override
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }



      @Override
    protected void configure(HttpSecurity http) throws Exception {
          // 禁用CSRF 开启跨域
          http.csrf().disable().cors();
        http
                .authorizeRequests()
//                .antMatchers("/product/**").hasRole("user")//部分请求需要某个角色才能访问,角色大小写要注意
//                .antMatchers("/admin/**").hasRole("admin1")
//                .antMatchers("/hello*").authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setSecurityMetadataSource(filterMetadataSource);
                o.setAccessDecisionManager(myAccessDecisionManager);
                return o;
            }
        }) // 定义哪些URL需要被保护、哪些不需要被保护
                .anyRequest().authenticated()// 任何请求,登录后可以访问
               .and().httpBasic()//http支持基础认证
              . and().formLogin()// 定义当需要用户登录时候,转到的登录页面。默认是spring-security自带的
              // .successForwardUrl("/hello")//登录成功默认跳转的地址
        ;
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/vercode");//忽略某些地址
    }
}

这样,基本就实现了动态url配置了

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值