SSM 和 SpringBoot 分别如何整合 SpringSecurity?

Spring Security

Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经发展了多年了,但是使用的并不多,安全管理这个领域,一直是 Shiro 的天下。

在 SSM/SSH 中整合 Spring Security 都是比较麻烦的操作,Spring Security 虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多(Shiro 虽然功能没有 Spring Security 多,但是对于大部分项目而言,Shiro 也够用了)。

自从有了 Spring Boot 之后,Spring Boot 对于 Spring Security 提供了 自动化配置方案,可以零配置使用 Spring Security。

因此,一般来说,常见的安全管理技术栈的组合是这样的(推荐):

  • SSM + Shiro
  • Spring Boot/Spring Cloud + Spring Security

SSM和SpringSecurity的配置

导包


<dependency>
    <groupId>org.springframework.securitygroupId>
    <artifactId>spring-security-configartifactId>
    <version>5.3.2.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframework.securitygroupId>
    <artifactId>spring-security-taglibsartifactId>
    <version>5.3.2.RELEASEversion>
dependency>

配置web.xml



<filter>
	<filter-name>springSecurityFilterChainfilter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
    <filter-name>springSecurityFilterChainfilter-name>
    <url-pattern>/*url-pattern>
filter-mapping>

配置spring-security.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
			    http://www.springframework.org/schema/beans/spring-beans.xsd
			    http://www.springframework.org/schema/context
			    http://www.springframework.org/schema/context/spring-context.xsd
			    http://www.springframework.org/schema/aop
			    http://www.springframework.org/schema/aop/spring-aop.xsd
			    http://www.springframework.org/schema/tx
			    http://www.springframework.org/schema/tx/spring-tx.xsd
			    http://www.springframework.org/schema/mvc
			    http://www.springframework.org/schema/mvc/spring-mvc.xsd
                http://www.springframework.org/schema/security
			    http://www.springframework.org/schema/security/spring-security.xsd">
    
    
	<security:http auto-config="true" use-expressions="true">
        
		<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
        
		<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
	security:http>
    
	<security:authentication-manager>
		<security:authentication-provider>
			<security:user-service>
				<security:user name="Jsu" password="{noop}0000"
                              authorities="ROLE_USER"/>
				<security:user name="Admin" password="{noop}0000"
                              authorities="ROLE_Admin"/>
			security:user-service>
		security:authentication-provider>
	security:authentication-manager>
beans>

将spring-security.xml 配置文件引入applicationContext.xml中


<import resource="classpath:spring-security.xml"/>

SpringSecurity使用自定义认证页面

spring-security.xml配置


<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>


<security:http auto-config="true" use-expressions="true">
    
    <security:intercept-url pattern="/login.jsp" access="permitAll()"/>
    
    <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
    
    <security:form-login login-page="/login.jsp"
                         login-processing-url="/login"
                         default-target-url="/index.jsp"
                         authentication-failure-url="/failer.jsp"/>
 security:http>

页面修改

<form action="${pageContext.request.contextPath}/login" method="post">

CSRF防护机制

CSRF(Cross-site request forgery) 跨站请求伪造,是一种难以防范的网络攻击方法

SpringSecurity中CsrfFilter过滤器

SpringSecurity的csrf机制将请求方式分为两类处理

  • 第一类:“GET”,“HEAD”,“TRACE”,"OPTIONS"四类请求可以直接通过

  • 第二类:除了上面四类,包括POST都要被验证携带token才可以通过

方法

  • 方法一:直接禁用csrf,不推荐

  • 方法二:在认证页面携带token请求

禁用csrf防护机制
<security:http auto-config="true" use-expressions="true">
	
	<security:csrf disabled="true"/>
security:http>
认证页面携带token
<%-- 在头部加上 --%>
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
<%-- form表单中加 --%>
<security:csrfInput/>

在这里插入图片描述

SpringSecurity使用数据库数据完成认证(加密)

配置spring-security.xml



<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

  
<security:authentication-manager>
    <security:authentication-provider user-service-ref="userServiceImpl"> 
        <security:password-encoder ref="passwordEncoder"/>
    security:authentication-provider>
security:authentication-manager>

UserService

// 继承UserDetailsService
public interface UserService extends UserDetailsService {
}

UserServiceImpl

public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleService roleService;
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    public void save(SysUser user) {
        // 加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userDao.save(user);
    }
    /**
     * 功能描述: 认证业务
     * @Param: [username] 用户在浏览器中输入的用户名
     * @Return: UserDetails 是springSecurity自己的用户对象
     * @Author: Jsu
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try { // 避免有两个username相同的
            // 根据用户名进行查询
            SysUser sysUser = userDao.findByName(username);
            // 查询失败
            if (sysUser == null) {
                // 认证失败
                return null;
            }
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            // 将当前用户的角色添加进入
            List<SysRole> roles = sysUser.getRoles();
            for (SysRole role : roles) {
                authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
            }
            //{noop}+sysUser.getPassword() 不被加盐加密 sysUser.getPassword() 加盐加密
           UserDetails userDetails = new User(sysUser.getUsername(),sysUser.getPassword(),authorities);
            return userDetails;
        } catch (Exception e) {
            e.printStackTrace();
            // 认证失败
            return null;
        }
    }
}

用户状态Status

在开发中有些用户 会被禁用 但是不会删除数据库

在这里插入图片描述

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    try { // 避免有两个username相同的
        // 根据用户名进行查询
        SysUser sysUser = userDao.findByName(username);
        // 查询失败
        if (sysUser == null) {
            // 认证失败
            return null;
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        // 将当前用户的角色添加进入
        List<SysRole> roles = sysUser.getRoles();
        for (SysRole role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        }
        // {noop} 后面的密码 会认为是原文
        // boolean ernabled是否可用
        // boolean accountNonExpired账户是否失效
        // boolean credentialsNonExpired秘密是否失效
        // boolean accountNonLocked账户是否锁定
        // 四个都为true  就运行
        UserDetails userDetails = new User(sysUser.getUsername(),
                sysUser.getPassword(),
                sysUser.getStatus() == 1, // 重点!!!! 
                true,
                true,
                true,
                authorities);
        return userDetails;
    } catch (Exception e) {
        e.printStackTrace();
        // 认证失败
        return null;
    }
}

RemberMe

!!!name=“remember-me” value=“true”/“1”/“on”/"yes"都可以



<security:http auto-config="true" use-expressions="true">
	
	<security:remember-me token-validity-seconds="60"/>
security:http>

在这里插入图片描述

如何持久化

存数据库中(官方规定写法 固定不变!!)

CREATE TABLE `persistent_logins`(
username varchar(64) NOT NULL,
series varchar(64) NOT NULL,
token varchar(64) NOT NULL,
last_used timestamp NOT NULL,
PRIMARY KEY (series)
)ENGINE=InnoDB DEFAULT CHARSET=utf8

<security:remember-me
                data-source-ref="dataSource"
                remember-me-parameter="remember-me"
                token-validity-seconds="60"/>

显示当前认证用户名


或者

SpringBoot整合SpringSecurity

导包

   <properties>
        <java.version>1.8java.version>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <spring-boot.version>2.1.3.RELEASEspring-boot.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>tk.mybatisgroupId>
            <artifactId>mapper-spring-boot-starterartifactId>
            <version>2.1.5version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-tomcatartifactId>
        dependency>
        <dependency>
            <groupId>org.apache.tomcat.embedgroupId>
            <artifactId>tomcat-embed-jasperartifactId>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>${spring-boot.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.8.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.3.0.RELEASEversion>
                <configuration>
                    <mainClass>com.jsu.SpringSecurityApplicationmainClass>
                configuration>
                <executions>
                    <execution>
                        <id>repackageid>
                        <goals>
                            <goal>repackagegoal>
                        goals>
                    execution>
                executions>
            plugin>
        plugins>
    build>

配置application.yml

server:
   port: 8080
spring:
   mvc:
      view:
         prefix: /pages/
         suffix: .jsp
   datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/security_authority
      username: root
      password: 000

mybatis:
   type-aliases-package: com.jsu.entity
   configuration:
      map-underscore-to-camel-case: true
logging:
   level:
      com.jsu: debug

创建POJO

public class SysRole implements GrantedAuthority {
    private Integer id;
    private String roleName;
    private String roleDesc;
    /**
     JsonIgnore 表示该字段不参与json的转换
     */
    @JsonIgnore
    @Override
    public String getAuthority() {
        return roleName;
    }
    public SysRole() {
    }
    public SysRole(Integer id, String roleName, String roleDesc) {
        this.id = id;
        this.roleName = roleName;
        this.roleDesc = roleDesc;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    public String getRoleDesc() {
        return roleDesc;
    }
    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
}
public class SysUser implements UserDetails {
    /**
     * 加入自定义属性
     */
    private Integer id;
    private String username;
    private String password;
    private Integer status;
    private List<SysRole> roles;
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }
    @Override
    public String getPassword() {
        return password;
    }
    @Override
    public String getUsername() {
        return username;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
    public SysUser(Integer id, String username, String password, Integer status, List<SysRole> roles) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.status = status;
        this.roles = roles;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public List<SysRole> getRoles() {
        return roles;
    }
    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }
}

Mapper配置

public interface RoleMapper extends Mapper<SysUser> {

    @Select("select r.id ,r.role_name roleName,r.role_desc roleDesc from sys_role r ,sys_user_role ur where r.id = ur.rid and ur.uid = #{uid}")
    public List<SysRole> findByUid(Integer uid);
}

public interface UserMapper extends Mapper<SysRole> {
    @Select("select * from sys_user where username = #{username}")
    @Results({
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "roles",column = "id",javaType = List.class,
            many = @Many(select = "com.jsu.mapper.RoleMapper.findByUid"))
    })
    public SysUser findByName(String username);

}

Service配置

public interface IUserService extends UserDetailsService {
}

@Service
@Transactional
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        return userMapper.findByName(s);
    }
}

@Controller
@RequestMapping("/product")
public class ProductController {
    @Secured("ROLE_PRODUCT")
    @RequestMapping("/findAll")
    public String findAll(){
        return "product-list";
    }
}

config配置

@Configuration
@EnableWebSecurity
// 使用security 内置的方式 开启
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private IUserService userService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    };

    /**
     * 认证用户的来源[内存 / 数据库]
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 配置类中的权限  不要加前缀
        // auth.inMemoryAuthentication()
        //         .withUser("user")
        //         .password("{noop}123")
        //         .roles("USER");

        // 使用自己数据库的数据 进行认证
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }
    /**
     * 配置springSecurity相关信息
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 释放静态资源  指定资源拦截规则 指定自定义认证页面  指定退出认证配置 csrf配置
        http.authorizeRequests()
                // 释放静态资源
                .antMatchers("/login.jsp","/failer.jsp","/css/**","/img/**","/plugins/**")
                .permitAll()
                // 指定拦截器规则
                .antMatchers("/**").hasAnyRole("USER","ADMIN")
                .anyRequest()
                .authenticated()
                .and()
                // 登录 映射login 跳转自定义login.jsp  成功跳转index.jsp 失败跳转failer.jsp
                .formLogin()
                .loginPage("/login.jsp")
                .loginProcessingUrl("/login")
                .successForwardUrl("/index.jsp")
                .failureForwardUrl("/failer.jsp")
                .permitAll()
                .and()
                // 退出登录 成功跳转登录页面
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login.jsp")
                // 清理缓存
                .invalidateHttpSession(true)
                .and()
                .csrf()
                .disable();

    }
}

配置拦截器

@ControllerAdvice
public class HandlerControllerException {

    @ExceptionHandler(RuntimeException.class)
    public String handlerException(RuntimeException e){
        if (e instanceof AccessDeniedException){
            return "forward:/403.jsp";
        }else {
            return "forward:/500.jsp";
        }
    }
}

er.jsp")
.permitAll()
.and()
// 退出登录 成功跳转登录页面
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login.jsp")
// 清理缓存
.invalidateHttpSession(true)
.and()
.csrf()
.disable();

}

}


## 配置拦截器

````java
@ControllerAdvice
public class HandlerControllerException {

    @ExceptionHandler(RuntimeException.class)
    public String handlerException(RuntimeException e){
        if (e instanceof AccessDeniedException){
            return "forward:/403.jsp";
        }else {
            return "forward:/500.jsp";
        }
    }
}

你可能感兴趣的:(SSM,spring)