springsecurity在springboot项目中的配置

springsecurity在springboot项目中的配置

1 jsp做页面展示

1.1 包

 <!--使用jsp必须打war包,表明这是一个web项目-->
<packaging>war</packaging>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>

    <!--jsp页面的el标签-->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--通用mapper-->
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.0.4</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

1.2 application.yml配置文件

配置很简单,不做解释

server:
  port: 8080
spring:
  mvc:
    view:
      prefix: /pages/
      suffix: .jsp

  datasource:
    url: jdbc:mysql:///security_authority?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password:
    driver-class-name: com.mysql.cj.jdbc.Driver


mybatis:
  type-aliases-package: cn.lx.security.doamin
  configuration:
    #驼峰
    map-underscore-to-camel-case: true

logging:
  level:
    cn.lx.security: debug

1.3 日志的配置文件log4j.properties

这个可以去log4j官网找

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=../logs/wlanapi/client.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

1.4 在main下面创建一个webapp包

项目会自动将包标点,如此,web项目创建成功

将项目需要的jsp,css等资源复制到webapp下,使用代码生成工具生成三层结构,并实现对用户,角色,权限的增删改查,这个太简单了,而且不是我们的重点,这里我们就默认已经搭建好了。接下来我们就来实现对security的配置。

1.5 security的前置准备

1.5.1 修改SysUser

SysUser直接UserDetails接口,这样我们在loadUserByUsername方法中就不用创建UserDetails实现类User的对象了,而是直接使用SysUser,非常方便,注意,要添加一个额外的属性,就是该用户拥有的权限的list集合

@Data
@Table(name = "sys_user")
public class SysUser implements UserDetails {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @Column(name = "status")
    private Integer status;


    //权限的集合对象
    private List<SysPermission> authorities;



    /**
     * Returns the authorities granted to the user. Cannot return <code>null</code>.
     *
     * @return the authorities, sorted by natural key (never <code>null</code>)
     */
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    /**
     * Indicates whether the user's account has expired. An expired account cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user's account is valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is locked or unlocked. A locked user cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
     */
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * Indicates whether the user's credentials (password) has expired. Expired
     * credentials prevent authentication.
     *
     * @return <code>true</code> if the user's credentials are valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * Indicates whether the user is enabled or disabled. A disabled user cannot be
     * authenticated.
     * 4个布尔值必须全为true,用户才算认证通过,这几个上篇文档已经讲过了
     * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
     */
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return status==1;
    }
}
1.5.2 修改SysPermission

我们让SysPermission实现GrantedAuthority接口,这样在loadUserByUsername方法中就不用了重新封装权限了

@Data
@Table(name = "sys_permission")
public class SysPermission implements GrantedAuthority {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "ID")
    private Integer id;

    @Column(name = "permission_NAME")
    private String permissionName;

    @Column(name = "permission_url")
    private String permissionUrl;

    @Column(name = "parent_id")
    private String parentId;

    /**
     * If the <code>GrantedAuthority</code> can be represented as a <code>String</code>
     * and that <code>String</code> is sufficient in precision to be relied upon for an
     * access control decision by an {@link AccessDecisionManager} (or delegate), this
     * method should return such a <code>String</code>.
     * <p>
     * If the <code>GrantedAuthority</code> cannot be expressed with sufficient precision
     * as a <code>String</code>, <code>null</code> should be returned. Returning
     * <code>null</code> will require an <code>AccessDecisionManager</code> (or delegate)
     * to specifically support the <code>GrantedAuthority</code> implementation, so
     * returning <code>null</code> should be avoided unless actually required.
     *
     * @return a representation of the granted authority (or <code>null</code> if the
     * granted authority cannot be expressed as a <code>String</code> with sufficient
     * precision).
     * @JsonIgnore 这个注解这个注解的作用是在序列化的时候忽略authority字段
     */
    @JsonIgnore
    @Override
    public String getAuthority() {
        return permissionName;
    }
}
1.5.3 修改IUserService接口

继承UserDetailsService接口,并实现

@Service
public class IUserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;


    /**
     * Locates the user based on the username. In the actual implementation, the search
     * may possibly be case sensitive, or case insensitive depending on how the
     * implementation instance is configured. In this case, the <code>UserDetails</code>
     * object that comes back may have a username that is of a different case than what
     * was actually requested..
     *
     * @param username the username identifying the user whose data is required.
     * @return a fully populated user record (never <code>null</code>)
     * @throws UsernameNotFoundException if the user could not be found or the user has no
     *                                   GrantedAuthority
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //这里是不是很简单
        SysUser sysUser = userMapper.findByUsername(username);
        if (null==sysUser){
            throw new RuntimeException("没有此用户");
        }
        return sysUser;
    }
}
1.5.4 修改UserMapper
//这里不仅仅只是查询出用户信息,还要查询用户拥有的权限
SysUser sysUser = userMapper.findByUsername(username);

所以我们可以直接这样,不理解的自己去查一下资料

@Select("select * from sys_user where username=#{username}")
    @Results({@Result(id = true, property = "id", column = "id"),
            @Result(property = "authorities", column = "id", javaType = List.class,
                    many = @Many(select = "cn.lx.security.dao.RermissionMapper.findByUid"))})
    public SysUser findByUsername(String username);

然后我们在RermissionMapper定义一个方法

//根据用户id查询该用户所拥有的权限
@Select("SELECT * FROM sys_permission WHERE ID IN(" +
            "SELECT PID FROM sys_role_permission WHERE RID IN(" +
            "SELECT RID FROM sys_user_role WHERE uid=#{uid}" +
            "))")
    public List<SysPermission> findByUid(Integer uid);

1.6 配置security

新建一个security的配置类

@Configuration
@EnableWebSecurity
//spring的安全注解,推荐使用
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private IUserService iUserService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    /**
     * Used by the default implementation of {@link #authenticationManager()} to attempt
     * to obtain an {@link AuthenticationManager}. If overridden, the
     * {@link AuthenticationManagerBuilder} should be used to specify the
     * {@link AuthenticationManager}.
     *
     * <p>
     * The {@link #authenticationManagerBean()} method can be used to expose the resulting
     * {@link AuthenticationManager} as a Bean. The {@link #userDetailsServiceBean()} can
     * be used to expose the last populated {@link UserDetailsService} that is created
     * with the {@link AuthenticationManagerBuilder} as a Bean. The
     * {@link UserDetailsService} will also automatically be populated on
     * {@link HttpSecurity#getSharedObject(Class)} for use with other
     * {@link SecurityContextConfigurer} (i.e. RememberMeConfigurer )
     * </p>
     *
     * <p>
     * For example, the following configuration could be used to register in memory
     * authentication that exposes an in memory {@link UserDetailsService}:
     * </p>
     *
     * <pre>
     * &#064;Override
     * protected void configure(AuthenticationManagerBuilder auth) {
     * 	auth
     * 	// enable in memory based authentication with a user named
     * 	// &quot;user&quot; and &quot;admin&quot;
     * 	.inMemoryAuthentication().withUser(&quot;user&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;).and()
     * 			.withUser(&quot;admin&quot;).password(&quot;password&quot;).roles(&quot;USER&quot;, &quot;ADMIN&quot;);
     * }
     *
     * // Expose the UserDetailsService as a Bean
     * &#064;Bean
     * &#064;Override
     * public UserDetailsService userDetailsServiceBean() throws Exception {
     * 	return super.userDetailsServiceBean();
     * }
     *
     * </pre>
     *
     * @param auth the {@link AuthenticationManagerBuilder} to use
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中注册一个账号
        //auth.inMemoryAuthentication().withUser("user").password("{noop}123").roles("USER");
        //连接数据库,使用数据库中的账号
        auth.userDetailsService(iUserService).passwordEncoder(bCryptPasswordEncoder);


    }

    /**
     * Override this method to configure {@link WebSecurity}. For example, if you wish to
     * ignore certain requests.
     * 这些资源可以直接访问,不需要认证
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**",
                "/img/**",
                "/plugins/**",
                "/failer.jsp",
                "/login.jsp");
    }

    /**
     * Override this method to configure the {@link HttpSecurity}. Typically subclasses
     * should not invoke this method by calling super as it may override their
     * configuration. The default configuration is:
     *
     * <pre>
     * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
     * </pre>
     * 读过上一篇文档的应该对这些配置有所了解
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception if an error occurs
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()	//禁用csrf
                .authorizeRequests()
                //所有请求都需要认证
                .anyRequest().authenticated()
                .and()
                //配置登录相关
            .formLogin().loginPage("/login.jsp").loginProcessingUrl("/login").defaultSuccessUrl("/index.jsp").failureForwardUrl("/failer.jsp").permitAll()
                .and()
             //配置注销相关  
       .logout().logoutUrl("/logout").logoutSuccessUrl("/login.jsp").invalidateHttpSession(true).permitAll();
    }

1.7 方法授权控制

//具体加到那个方法上,我就不具体说了
@PreAuthorize(value = "hasAuthority('权限名')")

jsp上菜单控制上一篇文档已经给大家看过了,这里就不展示了

2 thymeleaf模板做页面展示

2.1 静态资源

在resources下面新建一个static文件夹,静态资源全部放这里面,具体的见下图

在这里插入图片描述

2.2 登录页面

只需要注意两个地方,其他的和jsp差不多

(1)thymeleaf的约束

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">

(2)静态资源的路径

不需要写static路径,程序会自动识别static下的静态资源

<link rel="stylesheet"
	href="/plugins/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet"
	href="/plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet"
	href="/plugins/ionicons/css/ionicons.min.css">
<link rel="stylesheet"
	href="/plugins/adminLTE/css/AdminLTE.css">
<link rel="stylesheet"
	href="/plugins/iCheck/square/blue.css">

2.3 页面跳转的控制器

必须要写一个控制器,通过controller访问页面,我们可以写一个mvc的配置类

@Configuration
//不要使用这个注解,他会禁用springboot的自动装配,导致很多默认配置失效
//@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    /**
     * Configure simple automated controllers pre-configured with the response
     * status code and/or a view to render the response body. This is useful in
     * cases where there is no need for custom controller logic -- e.g. render a
     * home page, perform simple site URL redirects, return a 404 status with
     * HTML content, a 204 with no content, and more.
     *
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/loginPage").setViewName("login");
    }
}

2.4 security的配置

//这些资源不需要认证即可访问
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/css/**",
                               "/img/**",
                               "/plugins/**",
                               "favicon.ico",
                               "/loginPage");
}

还要修改登录页面

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        //.antMatchers("/css/**", "/img/**", "/plugins/**").permitAll()
        .anyRequest().authenticated()
        .and()
        //.formLogin().loginPage("/login.jsp").loginProcessingUrl("/login").defaultSuccessUrl("/index.jsp").failureForwardUrl("/failer.jsp").permitAll()
        //我们这里是前后端分离,不需要默认跳转页面和登录失败的页面
        .formLogin().loginPage("/loginPage").loginProcessingUrl("/login").permitAll()
        .and()
        .logout().logoutUrl("/logout").logoutSuccessUrl("/loginPage").invalidateHttpSession(true).permitAll();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值