springSecurity-1

  • Post author:
  • Post category:其他



Spring Security前身是Acegi Security,是Spring项目组中用来提供安全服务的框架



两个主要操作


认证

:用户建立一个他所声明的主体。主体一般是指用户,设备或可以在你系统中执行动作的其他系统。可以使用xml配置文件认证或者使用数据库认证


授权

:指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主体已经有身份验证过程建立了。



基础入门


1–导入依赖

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>5.0.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>5.0.1.RELEASE</version>
</dependency>


web.xml文件创建filter(名称必须为:springSecurityFilterChain 不可改变)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
  <display-name>Archetype Created Web Application</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-security.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

</web-app>


(使用xml配置文件认证)spring security核心配置文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security.xsd">


    <!-- 配置不过滤的资源(静态资源及登录相关) -->
    <security:http security="none" pattern="/login.html" />
    <security:http security="none" pattern="/failer.html" />
    <security:http auto-config="true" use-expressions="false" >
        <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
        <security:intercept-url pattern="/**" access="ROLE_USER" />
        <!--自定义登陆页面,
            login-page 自定义登陆页面
            authentication-failure-url 用户权限校验失败之后才会跳转到这个页面,如果数据库中没有这个用户则不会跳转到这个页面。
            default-target-url 登陆成功后跳转的页面。 注:登陆页面用户名固定 username,密码 password,action:login
        -->
        <security:form-login login-page="/login.html"
                             login-processing-url="/login"
                             username-parameter="username"
                             password-parameter="password"
                             authentication-failure-url="/failer.html"
                             default-target-url="/success.html"
                             authentication-success-forward-url="/success.html"
        />
        <!-- 登出, invalidate-session 是否删除session logout-url:登出处理链接 logout-success-url:登出成功页面
            注:登出操作 只需要链接到 logout即可登出当前用户 -->
        <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login.jsp" />
        <!-- 关闭CSRF,默认是开启的 -->
        <security:csrf disabled="true" />
    </security:http>
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service><!--以下创建了两个用户,指定了用户名、密码以及角色-->
                <security:user name="user" password="{noop}user" authorities="ROLE_USER" />
                <security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>


(使用数据库认证,即用户存在数据库中)有很多种,这里使用UserDetails、UserDetailsSevice来完成操作


在Spring Security中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用UserDetails、UserDetailsService来完成操作。



UserDetails

:UserDetails是一个接口,我们可以认为UserDetails作用是于封装当前进行认证的用户信息但由于其是一个接口,所以我们可以对其进行实现,也可以使用Spring Security提供的一个UserDetails的实现类User来完成操作

//UserDetails接口
public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

//user类部分代码
public class User implements UserDetails, CredentialsContainer {
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired; //帐户是否过期
    private final boolean accountNonLocked; //帐户是否锁定
    private final boolean credentialsNonExpired; //认证是否过期
    private final boolean enabled; //帐户是否可用//UserDetailsService接口代码
public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 
}


例子:用户管理中用户登录来完成Spring Security的认证操作


1-web.xml配置文件配置Security过滤器

<!-- 配置加载类路径的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
</context-param>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 配置监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2-spring security配置文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 配置不拦截的资源 -->
    <security:http pattern="/login.jsp" security="none"/>
    <security:http pattern="/failer.jsp" security="none"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/plugins/**" security="none"/>

    <!--
    	配置具体的规则
    	auto-config="true"	不用自己编写登录的页面,框架提供默认登录页面
    	use-expressions="false"	是否使用SPEL表达式(没学习过)
    -->
    <security:http auto-config="true" use-expressions="false">
        <!-- 配置具体的拦截的规则 pattern="请求路径的规则" access="访问系统的人,必须有ROLE_USER的角色" -->
        <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>

        <!-- 定义跳转的具体的页面
              login-page:登录界面
              default-target-url:登录成功目标url
              authentication-failure-url:登录失败
              authentication-success-forward-url:登录成功跳转页面
        -->
        <security:form-login
                login-page="/login.jsp"
                login-processing-url="/login.do"
                default-target-url="/index.jsp"
                authentication-failure-url="/failer.jsp"
                authentication-success-forward-url="/pages/main.jsp"
        />
        <!-- 关闭跨域请求 -->
        <security:csrf disabled="true"/>

        <!-- 退出 -->
        <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp"/>

    </security:http>

    <!-- 切换成数据库中的用户名和密码 -->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userService">  <!-- 通过这里配置的service找到对应的认证方法 -->
            <!-- 配置加密的方式 -->
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- 配置加密类 -->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <!-- 提供了入门的方式,在内存中存入用户名和密码
    <security:authentication-manager>
    	<security:authentication-provider>
    		<security:user-service>
    			<security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/>
    		</security:user-service>
    	</security:authentication-provider>
    </security:authentication-manager>
    -->

</beans>

3-自定义接口继承UserDetailsService接口

public interface IUserService extends UserDetailsService{}

4-实现类实现重写loadUserByUsername方法

@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService{

    @Autowired
    private IUserDao userDao;

    //使用该service方法完成认证
    @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()));
        User user = new User(userInfo.getUsername(),userInfo.getPassword(),userInfo.getStatus()==0?false:true,true,true,true,getAuthority(userInfo.getRoles()));
        return user;
    }
    //作用就是发挥一个list集合,集合装入的是角色描述
    public List<SimpleGrantedAuthority> getAuthority(List<Role> roles){
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        for (Role role:roles){
            list.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
        }
        return list;
    }
}

5-dao层的处理

public interface IUserDao {
    @Select("select * from users where username=#{username}")
    public UserInfo findByUsername(String username) throws Exception;//UserInfo 为自己定义的user实体类
}



服务器端方法级别权限控制



服务器端我们可以通过Spring security提供的注解对方法来进行权限控制。



三种类型的注解

Spring Security在方法的权限控制上支持三种类型的注解:

JSR-250注解 
@Secured注解
支持表达式的注解

这三种注解默认都是没有启用的,需要单独通过global-method-security元素的对应属性进行启用



开启注解

1-通过配置文件开启

<security:global-method-security jsr250-annotations= "enabled"/>
<security:global-method-security secured-annotations= "enabled"/>
<security:global-method-security pre-post annotations= "disabled"/>

2-通过注解开启


@EnableGlobalMethodSecurity

: Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,并在该类中将AuthenticationManager定义为Bean。



使用步骤



1-JSR-250注解

第一步:在security的xml配置文件中开启

<security:global-method-securityjsr256-annotationse="enabled"></security:global-method-security>

第二步:在指定方法上使用

// 查询所有产品
@RequestMapping(value = "/findAll.do")
@RolesAllowed("ADMIN")//该方法只有拥有ADMIN角色的用户可以访问
public ModelAndView findAll() throws Exception {
    ModelAndView mv = new ModelAndView();
    List<Product> ps = productService.findAll();
    mv.addObject("productList",ps);
    mv.setViewName("product-list1");
    return mv;
}


@RolesAllowed

表示访问对应方法时所应该具有的角色

示例:@RolesAllowed({“USER”,“ADMIN”})

该方法只要具有 “USER”, “ADMIN”任意一种权限就可以访问。这里可以省略前缀ROLE_ ,实际的权限可能是ROLE_ ADMIN


@PermitAIl

表示允许所有的角色进行访问, 也就是说不进行权限控制


@DenyAll

是和PermitAll相反的, 表示无论什么角色都不能访问

第三步:必须在pom.xml文件中导入注解的依赖(即jar包)

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>jsr250-api</artifactId>
    <version>1.0</version>
</dependency>



2-@Secured注解

(由于是security提供,所以不用额外导入jar包)

第一步:在security的xml配置文件中开启注解

<security:global-method-security secured-annotations="enabled"></security:global-method-security>

第二步:在指定方法上使用

//查询所以订单--分页
@RequestMapping("/findAll.do")
@Secured("ROLE_ADMIN")//必须加上前缀ROLE_
public ModelAndView findAll(
        @RequestParam(name = "page",required = true,defaultValue = "1")int page,
        @RequestParam(name = "pageSize",required = true,defaultValue = "4")int size) throws Exception {
    ModelAndView mv = new ModelAndView();
    List<Orders> ordersList = ordersService.findAll(page,size);
    //pageinfo就是 一个分页bean
    PageInfo pageInfo = new PageInfo(ordersList);
    mv.addObject("pageInfo",pageInfo);
    mv.setViewName("orders-page-list");
    return mv;
}


@Secured

注解标注的方法进行权限控制的支持,其值默认为disabled,

示例:

@Secured( "IS_AUTHENTICATED_ ANONYMOUSLY")
public Account readAccount(Long id);
@Secured( "ROLE_TELLER")


注意:在使用JSR25主解时。可以省略ROLE而我们现在使用的secured是不能省略前缀



3-支持表达式的注解

第一步:在security的xml配置文件中开启注解

<security:global-method-security pre-post-annotations="enabled" ></security:global-method-security>

第二步:在指定方法上使用(可以使用SPEL表达式)

@RequestMapping("/findAll.do")
@PreAuthorize("hasRole(ROLE_ADMIN)")//只有具有ROLE_ADMIN角色的用户可以进行添加用户操作
public ModelAndView findAll() throws Exception {
    ModelAndView mv = new ModelAndView();
    List<UserInfo> userList= userService.findAll();
    mv.addObject("userList",userList);
    mv.setViewName("user-list");
    return mv;
}

//添加用户
@RequestMapping("/save.do")
@PreAuthorize("authentication. principal. username='tom'")//只有名叫tom的用户可以进行添加用户操作
public String save(UserInfo userInfo) throws Exception {
    userService.save(userInfo);
    return "redirect:findAll.do";
}


@PreAuthorize

在方法调用之前,基于表达式的计算结果来限制对方法的访问

示例:

@PreAuthorize("#userId == authentication. principal. userId or hasAuthority(‘ADMIN')")
void changePassword(@P( "userId") long userId ){ }

这里表示在changePassword方法执行之前,判断方法参数userId的值是否等于principa1中保存的当前用户的

userId,或者当前用户是否具有ROLE_ ADMIN权限,两种符合其一, 就可以访问该方法。


@PostAuthorize

允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常

示例:

@PostAuthorize
User getUser(" return0bject.userId == authentication. principal .userId or
hasPermission( returnObject,' ADMIN')");


@PostFilter

允许方法调用,但必须按照表达式来过滤方法的结果


@PreFilter

允许方法调用,但必须在进入方法之前过滤输入值



页面端权限控制




导入依赖

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>version</version>
</dependency>



jsp页面导入标签

<%etaglib prefix="security" uri="http://www.springframework.org/security/tags" %>



常用标签

**

authentication

**可以获取当前正在操作的用户信息

<%-- 当前正在操作的用户 --%>
<security:authentication property="principal.username"></security:authentication>


效果预览




**

authorize

**用于控制页面上某些标签是否可以显示

<security:authorize access="hasRole('ADMIN')">
<a href="${pageContext.request.contextPath}/user/findAll.do">
    <i class="fa fa-circle-o"></i> 用户管理
</a>
</security:authorize>

如果登录用户没有admin角色的话无法查看到该项。

使用该标签在security的xml配置文件中需要把 <security:http auto-config=“true” use-expressions=“true”>中的use-expressions设置为true(即使用el表达式)






效果预览






页面使用表达式

  • 1-可以通过security的xml配置文件中<security:http auto-config=“true” use-expressions=“true”>中的use-expressions设置为true(即使用el表达式)*
  • 2-或者在security的xml配置文件中多配置一个bean
<bean id="webexpressionHandler"
      class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />



版权声明:本文为qq_41617261原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。