SpringSecurity安全认证服务框架


角色权限控制

基于UserDetails 数据库认证

  • Maven导入依赖
        
            org.springframework.security
            spring-security-web
            5.0.1.RELEASE
        
        
            org.springframework.security
            spring-security-config
            5.0.1.RELEASE
        
        
            org.springframework.security
            spring-security-core
            5.0.1.RELEASE
        
        
            org.springframework.security
            spring-security-taglibs
            5.0.1.RELEASE
        


  • web.xml配置


    
    
        contextConfigLocation
        classpath:spring-security.xml
    
    
        
        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        springSecurityFilterChain
        /*
    


  • spring-security两种配置方式

1.xml配置
2.实体类配置

(1) spring-security.xml配置文件




    
    
    
    
    
    
    
    
        
        

        
        

        
        
        
        

    

    
    
        
            

        
    

    



    

(2)实体类配置方式

@Component
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {


    /****
     * 1、放行配置
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //放行地址
        web.ignoring().antMatchers("/css/**");
        web.ignoring().antMatchers("/img/**");
        web.ignoring().antMatchers("/js/**");
        web.ignoring().antMatchers("/plugins/**");
        web.ignoring().antMatchers("/*.html");
        web.ignoring().antMatchers("/seller/add.shtml");
    }

    /***
     * 2、权限拦截配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //限制访问
        http.authorizeRequests().antMatchers("/**").access("hasAnyRole('ADMIN')");

        //禁用CSRF
        http.csrf().disable();


        //发生异常
        http.exceptionHandling().accessDeniedPage("/error.html");

        //启用iframe
        http.headers().frameOptions().disable();

        //一个用户只允许在一个地方登录,其他用户登录就会把已登录用户挤掉
        http.sessionManagement().maximumSessions(1).expiredUrl("/shoplogin.html");

        //配置登录
        http.formLogin().loginPage("/shoplogin.html")
                .loginProcessingUrl("/login")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        //成功响应消息
                        Result result = new Result(true, "/admin/index.html");
                        responseLogin(response, result);
                    }
                }).failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        //失败响应消息
                        Result result = new Result(false, "账号或密码不正确!");
                        responseLogin(response, result);
                    }
                });

        //.defaultSuccessUrl("/admin/index.html",true)
        //.failureUrl("/shoplogin.html");

        //配置登出
        http.logout().logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/shoplogin.html");

    }

    /***
     * 响应用户登录
     * @param response
     * @param result
     * @throws IOException
     */
    public void responseLogin(HttpServletResponse response, Result result) throws IOException {
        //设置编码格式
        response.setContentType("application/json;charset=utf-8");

        //将Result转成JSON字符
        String jsonString = JSON.toJSONString(result);

        //输出数据
        PrintWriter writer = response.getWriter();
        writer.write(jsonString);

        writer.flush();
        writer.close();
    }


    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder encoder;

    /***
     * 3、授权认证
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //写死
        //auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");

        //自定义授权认证类
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(encoder);  //指定加密对象
    }


  • userService实现类

自定义IUserService 类 继承 UserDetailsService

package com.itheima.ssm.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface IUserService extends UserDetailsService {
}

使用 自定义UserServiceImpl 类实现 IUserService的接口

package com.itheima.ssm.service.impl;

import com.itheima.ssm.dao.IUserDao;
import com.itheima.ssm.domain.Role;
import com.itheima.ssm.domain.UserInfo;
import com.itheima.ssm.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

//注意'userService'与spring-security.xml 里面的同名
//
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        try {
            userInfo = userDao.findByUsername(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //处理自己的用户对象封装成UserDetails
        //  User user=new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthority(userInfo.getRoles()));
        //由于密码是明文,不安全的,要加上解析"{noop}"
        User user = new User(userInfo.getUsername(), "{noop}" + userInfo.getPassword(), userInfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userInfo.getRoles()));
        return user;
    }
    //作用就是返回一个List集合,集合中装入的是角色描述
    public List getAuthority(List roles) {

        List list = new ArrayList<>();
        for (Role role : roles) {
            list.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleName()));
        }
        return list;
    }
}


对密码进行加密 BCryptPasswordEncoder

修改spring-secuity.xml,配置BCryptPasswordEncoder类("id = passwordEncoder"),并引用到数据库配置。

    
    
        
            
            
        
    

    
    

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public void save(UserInfo userInfo) throws Exception{
        String newPassword = bCryptPasswordEncoder.encode(userInfo.getPassword());
        userInfo.setPassword(newPassword);
        userDao.save(userInfo);
    }

此时 可以去掉密码"{noop}"声明

public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = null;
        try {
            userInfo = userDao.findByUsername(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //处理自己的用户对象封装成UserDetails
        //  User user=new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthority(userInfo.getRoles()));
        //由于密码是明文,不安全的,要加上解析"{noop}"
        User user = new User(userInfo.getUsername(), userInfo.getPassword(), userInfo.getStatus() == 0 ? false : true, true, true, true, getAuthority(userInfo.getRoles()));
        return user;
    }
}


方法级别权限控制

  • JSR250

注解:
@RolesAllowed(常用) 表示访问对应方法时所应该具有的角色
@PermitAll 表示允许所有角色进行访问,即不进行权限控制
@DenyAll 与PermitAll 相反,无论什么角色都不能访问

  • maven导入依赖
        
            javax.annotation
            jsr250-api
            1.0
        
  • spring-security.xml 开启命令支持
    
public class ProductController{

    @RequestMapping("/findAll.do")
    @RolesAllowed("ADMIN") //只有ADMIN角色可用
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List ps = productService.findAll();
        mv.addObject("productList", ps);
        mv.setViewName("product-list1");
        return mv;
    }
}


  • Secured注解

1.属于spring框架,无需导入依赖
2.相比JSR250,权限必须加上"ROLE_XXX",例如 ROLE_ADMIN

  • spring-security.xml开启命令支持

public class OrdersController {

    @Autowired
    private IOrdersService ordersService;

    @RequestMapping("/findAll.do")
    @Secured("ROLE_ADMIN") //此处ROLE必须加上
    public ModelAndView findAll(@RequestParam(name = "page", required = true, defaultValue = "1") int page, @RequestParam(name = "size", required = true, defaultValue = "4") int size) throws Exception {
        ModelAndView mv = new ModelAndView();
        List ordersList = ordersService.findAll(page, size);
        //PageInfo就是一个分页Bean
        PageInfo pageInfo = new PageInfo(ordersList);
        mv.addObject("pageInfo", pageInfo);
        mv.setViewName("orders-page-list");
        return mv;
    }


  • 表达式注解

1.@PreAuthorize在方法调用前,基于表达式的计算结果来限制 对方法的访问
示例:
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority('ADMIN')")
void changePassword(@P("userId") long userId){ }

2.@PostAuthorize允许方法调用,但是如果表达式计算结果为false,将抛出异常
示例:
@PostAuthorize
User getUser(returnObject.serId==authentication.principal.userId or hasPermission(returnObject,'ADMIN'));

  • spring-security.xml开启

   @RequestMapping("/save.do")
    //只有当前登录是tom用户 才可以操作
    @PreAuthorize("authentication.principal.username=='tom'")
    public String save(UserInfo userInfo) throws Exception{
        userService.save(userInfo);
        return "redirect:findAll.do";
    }

    @RequestMapping("/findAll.do")
    //只有admin用于才可以操作
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        List userList = userService.findAll();
        mv.addObject("userList",userList);
        mv.setViewName("user-list");
        return mv;
    }

Spring Security允许我们在定义URL访问或方法访问所应有的权限时使用Spring EL表达式,在定义所需的访问权限时如果对应的表达式返回结果为true则表示拥有对应的权限,反之则无。Spring Security可用表达式对象的基类是SecurityExpressionRoot,其为我们提供了如下在使用Spring EL表达式对URL或方法进行权限控制时通用的内置表达式。

表达式 描述
hasRole([role]) 当前用户是否拥有指定角色。
hasAnyRole([role1,role2]) 多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。
hasAuthority([auth]) 等同于hasRole
hasAnyAuthority([auth1,auth2]) 等同于hasAnyRole
Principle 代表当前用户的principle对象
authentication 直接从SecurityContext获取的当前Authentication对象
permitAll 总是返回true,表示允许所有的
denyAll 总是返回false,表示拒绝所有的
isAnonymous() 当前用户是否是一个匿名用户
isRememberMe() 表示当前用户是否是通过Remember-Me自动登录的
isAuthenticated() 表示当前用户是否已经登录认证成功了。
isFullyAuthenticated() 如果当前用户既不是一个匿名用户,同时又不是通过Remember-Me自动登录的,则返回true。


页面端 权限控制

  • 导入maven依赖
        
            org.springframework.security
            spring-security-taglibs
            5.3.1.RELEASE
        
  • jsp页面引入
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
  • 页面标签关键词

authentication 获取当前正在操作的用户信息
authorize 控制页面某些标签是否显示

  • 示例 1
    jsp页面展示当前用户名

你可能感兴趣的:(SpringSecurity安全认证服务框架)